@@ -264,6 +269,33 @@
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.2.5
+
+ false
+
+ ${meos.enabled}
+
+
+ ${meos.lib.dir}
+
+ --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.base/sun.nio.ch=ALL-UNNAMED
+
+
+
+
\ No newline at end of file
diff --git a/flink-processor/src/main/java/aisdata/AISData.java b/flink-processor/src/main/java/aisdata/AISData.java
index 3153362..4d9035b 100644
--- a/flink-processor/src/main/java/aisdata/AISData.java
+++ b/flink-processor/src/main/java/aisdata/AISData.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package aisdata;
public class AISData {
diff --git a/flink-processor/src/main/java/aisdata/AISDataDeserializationSchema.java b/flink-processor/src/main/java/aisdata/AISDataDeserializationSchema.java
index 786799a..3d4a301 100644
--- a/flink-processor/src/main/java/aisdata/AISDataDeserializationSchema.java
+++ b/flink-processor/src/main/java/aisdata/AISDataDeserializationSchema.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package aisdata;
import com.fasterxml.jackson.core.JsonParser;
diff --git a/flink-processor/src/main/java/aisdata/AISTestSource.java b/flink-processor/src/main/java/aisdata/AISTestSource.java
index 9cbd32e..a40dfbd 100644
--- a/flink-processor/src/main/java/aisdata/AISTestSource.java
+++ b/flink-processor/src/main/java/aisdata/AISTestSource.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package aisdata;
import org.apache.flink.streaming.api.functions.source.SourceFunction;
diff --git a/flink-processor/src/main/java/aisdata/Main.java b/flink-processor/src/main/java/aisdata/Main.java
index ccc3458..d396a11 100644
--- a/flink-processor/src/main/java/aisdata/Main.java
+++ b/flink-processor/src/main/java/aisdata/Main.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package aisdata;
import java.time.Duration;
@@ -20,6 +45,7 @@
import org.apache.flink.streaming.api.windowing.time.Time;
import functions.*;
+import functions.GeneratedFunctions;
import types.boxes.*;
import types.basic.tpoint.tgeom.*;
import types.basic.tpoint.TPoint.*;
@@ -37,8 +63,8 @@ public static void main(String[] args) throws Exception {
// Initialize MEOS with proper error handling
try {
logger.info("Initializing MEOS library");
- functions.meos_initialize_timezone("UTC");
- functions.meos_initialize_error_handler(errorHandler);
+ GeneratedFunctions.meos_initialize_timezone("UTC");
+ GeneratedFunctions.meos_initialize_error_handler(errorHandler);
final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
@@ -93,7 +119,7 @@ public static void main(String[] args) throws Exception {
// Always ensure MEOS is finalized
try {
logger.info("Finalizing MEOS library");
- functions.meos_finalize();
+ GeneratedFunctions.meos_finalize();
} catch (Exception e) {
logger.error("Error during MEOS finalization: {}", e.getMessage(), e);
}
diff --git a/flink-processor/src/main/java/aisdata/TrajectoryWindowFunction.java b/flink-processor/src/main/java/aisdata/TrajectoryWindowFunction.java
index 409dd70..964c92c 100644
--- a/flink-processor/src/main/java/aisdata/TrajectoryWindowFunction.java
+++ b/flink-processor/src/main/java/aisdata/TrajectoryWindowFunction.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package aisdata;
import org.apache.flink.api.java.tuple.Tuple4;
@@ -13,6 +38,7 @@
import java.util.ArrayList;
import java.util.List;
import functions.*;
+import functions.GeneratedFunctions;
import types.boxes.*;
import types.basic.tpoint.tgeom.*;
import types.basic.tpoint.tgeom.TGeomPointSeq;
@@ -30,14 +56,15 @@ public class TrajectoryWindowFunction extends
public void open(Configuration parameters) throws Exception {
super.open(parameters);
errorHandler = new error_handler(); // Initialize error handler here
- functions.meos_initialize_timezone("UTC");
- functions.meos_initialize_error_handler(errorHandler);
+
+ GeneratedFunctions.meos_initialize_timezone("UTC");
+ GeneratedFunctions.meos_initialize_error_handler(errorHandler);
logger.info("MEOS initialized in TrajectoryWindowFunction.open()");
}
// @Override
// public void close() throws Exception {
- // functions.meos_finalize();
+ // GeneratedFunctions.meos_finalize();
// logger.info("MEOS finalized in TrajectoryWindowFunction.close()");
// super.close();
// }
diff --git a/flink-processor/src/main/java/berlinmod/BerlinMODCorpus.java b/flink-processor/src/main/java/berlinmod/BerlinMODCorpus.java
new file mode 100644
index 0000000..8848724
--- /dev/null
+++ b/flink-processor/src/main/java/berlinmod/BerlinMODCorpus.java
@@ -0,0 +1,182 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
+package berlinmod;
+
+import functions.GeneratedFunctions;
+import jnr.ffi.Pointer;
+import org.mobilitydb.flink.meos.wirings.MeosWiringRuntime;
+
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.time.OffsetDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.time.temporal.ChronoField;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.TreeSet;
+import java.util.stream.Stream;
+
+/**
+ * Corpus loader and query-parameter derivation for the BerlinMOD streaming
+ * benchmark.
+ *
+ * Supplies the real BerlinMOD instants corpus read from the
+ * {@code berlinmod_instants.csv} produced by the BerlinMOD generator, or a
+ * small bundled canonical sample ({@link #loadSample()}) for local drivers. Real instants are stored in EPSG:3857; they are
+ * reprojected to EPSG:4326 through MEOS {@code geo_transform} at load — the
+ * loader holds no projection mathematics of its own.
+ *
+ *
{@link Params} fixes the per-query parameters from the corpus itself (its
+ * centroid, bounding box, vehicle ids, and time span) so every spatial cell is
+ * selective and the windowing granularity yields a comparable number of windows
+ * regardless of the corpus time span.
+ */
+public final class BerlinMODCorpus {
+
+ private static final DateTimeFormatter TS = new DateTimeFormatterBuilder()
+ .appendPattern("yyyy-MM-dd HH:mm:ss")
+ .optionalStart().appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true).optionalEnd()
+ .appendOffset("+HH", "Z")
+ .toFormatter();
+
+ private BerlinMODCorpus() { /* utility */ }
+
+ /** Query parameters derived from a corpus. */
+ public static final class Params {
+ public final double pLon, pLat, radiusMetres, dMeetMetres;
+ public final double xmin, ymin, xmax, ymax;
+ public final double s1Lon, s1Lat, s2Lon, s2Lat;
+ public final List pois;
+ public final int targetId, xId, yId;
+ public final long windowSeconds, snapshotTickMillis;
+
+ Params(double pLon, double pLat, double radiusMetres, double dMeetMetres,
+ double xmin, double ymin, double xmax, double ymax,
+ double s1Lon, double s1Lat, double s2Lon, double s2Lat,
+ List pois, int targetId, int xId, int yId,
+ long windowSeconds, long snapshotTickMillis) {
+ this.pLon = pLon; this.pLat = pLat; this.radiusMetres = radiusMetres; this.dMeetMetres = dMeetMetres;
+ this.xmin = xmin; this.ymin = ymin; this.xmax = xmax; this.ymax = ymax;
+ this.s1Lon = s1Lon; this.s1Lat = s1Lat; this.s2Lon = s2Lon; this.s2Lat = s2Lat;
+ this.pois = pois; this.targetId = targetId; this.xId = xId; this.yId = yId;
+ this.windowSeconds = windowSeconds; this.snapshotTickMillis = snapshotTickMillis;
+ }
+ }
+
+ /** Real BerlinMOD instants from {@code berlinmod_instants.csv}
+ * (columns {@code tripid,vehid,day,seqno,geom,t}), reprojected 3857→4326
+ * through MEOS, sorted by timestamp. {@code maxRows <= 0} loads all rows. */
+ public static List fromInstantsCsv(String path, int maxRows) throws Exception {
+ MeosWiringRuntime.ensureInitializedOnThread();
+ List events = new ArrayList<>();
+ try (Stream lines = Files.lines(Paths.get(path))) {
+ java.util.Iterator it = lines.iterator();
+ if (it.hasNext()) {
+ it.next(); // header
+ }
+ while (it.hasNext() && (maxRows <= 0 || events.size() < maxRows)) {
+ String[] f = it.next().split(",");
+ int vid = Integer.parseInt(f[1].trim());
+ long ms = OffsetDateTime.parse(f[5].trim(), TS).toInstant().toEpochMilli();
+ Pointer g4326 = GeneratedFunctions.geo_transform(
+ GeneratedFunctions.geom_in(f[4].trim(), -1), 4326);
+ String txt = GeneratedFunctions.geo_as_text(g4326, 7); // POINT(lon lat)
+ String[] xy = txt.substring(txt.indexOf('(') + 1, txt.indexOf(')')).trim().split("\\s+");
+ events.add(make(vid, ms, Double.parseDouble(xy[0]), Double.parseDouble(xy[1])));
+ }
+ }
+ events.sort((a, b) -> Long.compare(a.getTimestamp(), b.getTimestamp()));
+ return events;
+ }
+
+ /** Derive selective per-query parameters and a window/tick granularity that
+ * yields ~200 windows over the corpus time span. */
+ public static Params derive(List corpus) {
+ double sumLon = 0, sumLat = 0, minLon = Double.MAX_VALUE, minLat = Double.MAX_VALUE,
+ maxLon = -Double.MAX_VALUE, maxLat = -Double.MAX_VALUE, minT = Double.MAX_VALUE, maxT = -Double.MAX_VALUE;
+ TreeSet ids = new TreeSet<>();
+ for (BerlinMODTrip t : corpus) {
+ sumLon += t.getLon(); sumLat += t.getLat();
+ minLon = Math.min(minLon, t.getLon()); maxLon = Math.max(maxLon, t.getLon());
+ minLat = Math.min(minLat, t.getLat()); maxLat = Math.max(maxLat, t.getLat());
+ minT = Math.min(minT, t.getTimestamp()); maxT = Math.max(maxT, t.getTimestamp());
+ ids.add(t.getVehicleId());
+ }
+ int n = corpus.size();
+ double cLon = sumLon / n, cLat = sumLat / n;
+ double exLon = maxLon - minLon, exLat = maxLat - minLat;
+ List idList = new ArrayList<>(ids);
+ int targetId = idList.get(idList.size() / 2);
+ int xId = idList.get(0);
+ int yId = idList.get(Math.min(idList.size() - 1, idList.size() / 2));
+ long span = (long) (maxT - minT);
+ long windowSeconds = Math.max(1L, span / 1000 / 200);
+ long tickMillis = Math.max(1000L, windowSeconds * 1000L / 2);
+ List pois = Arrays.asList(
+ new PointOfInterest(1, cLon, cLat, 2_000.0),
+ new PointOfInterest(2, cLon + 0.1 * exLon, cLat + 0.1 * exLat, 1_000.0),
+ new PointOfInterest(3, cLon - 0.1 * exLon, cLat - 0.1 * exLat, 2_000.0));
+ return new Params(cLon, cLat, 5_000.0, 5_000.0,
+ cLon - 0.25 * exLon, cLat - 0.25 * exLat, cLon + 0.25 * exLon, cLat + 0.25 * exLat,
+ minLon + 0.25 * exLon, cLat, maxLon - 0.25 * exLon, cLat,
+ pois, targetId, xId, yId, windowSeconds, tickMillis);
+ }
+
+ private static BerlinMODTrip make(int vid, long t, double lon, double lat) {
+ BerlinMODTrip trip = new BerlinMODTrip();
+ trip.setVehicleId(vid);
+ trip.setTimestamp(t);
+ trip.setLon(lon);
+ trip.setLat(lat);
+ return trip;
+ }
+ /**
+ * Loads the canonical BerlinMOD sample (real instants for vehicles 1-5,
+ * reprojected to WGS84 and time-aligned) bundled as a classpath resource.
+ * A slice of the single canonical BerlinMOD corpus shared across the
+ * ecosystem — no invented coordinates, no MEOS needed (pre-reprojected).
+ */
+ public static List loadSample() {
+ List events = new ArrayList<>();
+ try (java.io.BufferedReader r = new java.io.BufferedReader(new java.io.InputStreamReader(
+ BerlinMODCorpus.class.getResourceAsStream("/berlinmod_sample.csv"),
+ java.nio.charset.StandardCharsets.UTF_8))) {
+ r.readLine(); // header: vehicleId,timestampMs,lon,lat
+ for (String line; (line = r.readLine()) != null; ) {
+ String[] f = line.split(",");
+ events.add(make(Integer.parseInt(f[0].trim()), Long.parseLong(f[1].trim()),
+ Double.parseDouble(f[2].trim()), Double.parseDouble(f[3].trim())));
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("loading /berlinmod_sample.csv", e);
+ }
+ events.sort((a, b) -> Long.compare(a.getTimestamp(), b.getTimestamp()));
+ return events;
+ }
+
+}
diff --git a/flink-processor/src/main/java/berlinmod/BerlinMODDeserializationSchema.java b/flink-processor/src/main/java/berlinmod/BerlinMODDeserializationSchema.java
index a45b6d1..b11306f 100644
--- a/flink-processor/src/main/java/berlinmod/BerlinMODDeserializationSchema.java
+++ b/flink-processor/src/main/java/berlinmod/BerlinMODDeserializationSchema.java
@@ -1,3 +1,27 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
package berlinmod;
import com.fasterxml.jackson.core.JsonParser;
diff --git a/flink-processor/src/main/java/berlinmod/BerlinMODParity.java b/flink-processor/src/main/java/berlinmod/BerlinMODParity.java
new file mode 100644
index 0000000..debdc6f
--- /dev/null
+++ b/flink-processor/src/main/java/berlinmod/BerlinMODParity.java
@@ -0,0 +1,155 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
+package berlinmod;
+
+import org.apache.flink.api.common.eventtime.WatermarkStrategy;
+import org.apache.flink.api.java.tuple.Tuple3;
+import org.apache.flink.configuration.Configuration;
+import org.apache.flink.streaming.api.datastream.DataStream;
+import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
+import org.apache.flink.streaming.api.functions.sink.RichSinkFunction;
+
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.function.BiPredicate;
+
+/**
+ * Snapshot-form parity check for the BerlinMOD streaming benchmark.
+ *
+ * The streaming parity contract is that a streaming query computes the same
+ * result as a batch evaluation of the same MEOS predicate. This driver verifies
+ * it on the continuous form, which is timing-independent: the continuous form
+ * emits {@code predicate(event)} for every event, and a batch pass over the same
+ * corpus computes {@code predicate(event)} directly through the same
+ * {@link MEOSBridge} call. The two must agree event-for-event.
+ *
+ *
Checked queries: Q3 (within {@code d} of point {@code P}, MEOS
+ * {@code edwithin_tgeo_geo}) and Q8 (within {@code d} of a road segment, MEOS
+ * {@code edwithin_tgeo_geo} against a line). The corpus and parameters are the
+ * same as {@link BerlinMODBenchmark}.
+ *
+ *
+ * java … berlinmod.BerlinMODParity --csv <berlinmod_instants.csv> [--max N]
+ * java … berlinmod.BerlinMODParity --vehicles 50 --events 600
+ *
+ */
+public final class BerlinMODParity {
+
+ // Continuous-form outputs in arrival order (parallelism 1, no keyBy → stream
+ // order equals corpus order), so element i corresponds to corpus event i.
+ private static final ConcurrentLinkedQueue STREAMED = new ConcurrentLinkedQueue<>();
+
+ private BerlinMODParity() { /* utility */ }
+
+ public static void main(String[] args) throws Exception {
+ String csv = null;
+ int maxRows = 0;
+ for (int i = 0; i < args.length; i++) {
+ switch (args[i]) {
+ case "--csv": csv = args[++i]; break;
+ case "--max": maxRows = Integer.parseInt(args[++i]); break;
+ default: break;
+ }
+ }
+ if (csv == null) {
+ System.err.println("--csv is required: parity runs "
+ + "on the canonical BerlinMOD corpus only.");
+ System.exit(2);
+ }
+ List corpus = BerlinMODCorpus.fromInstantsCsv(csv, maxRows);
+ BerlinMODCorpus.Params p = BerlinMODCorpus.derive(corpus);
+ System.out.printf("Corpus: %s, %d events; P=(%.5f,%.5f) r=%.0fm%n",
+ csv != null ? "real BerlinMOD instants" : "synthetic", corpus.size(),
+ p.pLon, p.pLat, p.radiusMetres);
+
+ MeosWiringInit();
+ BiPredicate q3 = (lon, lat) ->
+ MEOSBridge.dwithinMetres(lon, lat, p.pLon, p.pLat, p.radiusMetres);
+ BiPredicate q8 = (lon, lat) ->
+ MEOSBridge.dwithinSegmentMetres(lon, lat, p.s1Lon, p.s1Lat, p.s2Lon, p.s2Lat, p.radiusMetres);
+
+ List rows = new ArrayList<>();
+ rows.add(check("Q3", corpus,
+ t -> t.process(new Q3ContinuousFunction(p.pLon, p.pLat, p.radiusMetres)), q3));
+ rows.add(check("Q8", corpus,
+ t -> t.process(new Q8ContinuousFunction(p.s1Lon, p.s1Lat, p.s2Lon, p.s2Lat, p.radiusMetres)), q8));
+
+ System.out.println();
+ System.out.println("| Query | Events | Streaming-true | Batch-true | Mismatches | Parity |");
+ System.out.println("|---|---:|---:|---:|---:|---|");
+ for (String[] r : rows) {
+ System.out.printf("| %s | %s | %s | %s | %s | %s |%n", r[0], r[1], r[2], r[3], r[4], r[5]);
+ }
+ }
+
+ private static String[] check(String query, List corpus,
+ java.util.function.Function, DataStream>> wiring,
+ BiPredicate batch) throws Exception {
+ STREAMED.clear();
+ StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
+ env.setParallelism(1);
+ DataStream trips = env.fromCollection(corpus)
+ .assignTimestampsAndWatermarks(WatermarkStrategy
+ .forBoundedOutOfOrderness(Duration.ofSeconds(1))
+ .withTimestampAssigner((e, ts) -> e.getTimestamp()));
+ wiring.apply(trips).addSink(new CollectSink());
+ env.execute("parity-" + query);
+
+ Boolean[] streamed = STREAMED.toArray(new Boolean[0]);
+ long streamingTrue = 0, batchTrue = 0, mismatches = 0;
+ for (int i = 0; i < corpus.size(); i++) {
+ boolean expected = batch.test(corpus.get(i).getLon(), corpus.get(i).getLat());
+ if (expected) {
+ batchTrue++;
+ }
+ if (i < streamed.length && streamed[i]) {
+ streamingTrue++;
+ }
+ if (i >= streamed.length || streamed[i].booleanValue() != expected) {
+ mismatches++;
+ }
+ }
+ boolean parity = mismatches == 0 && streamed.length == corpus.size();
+ System.out.printf(" %s: events=%d streaming-out=%d streaming-true=%d batch-true=%d mismatches=%d parity=%s%n",
+ query, corpus.size(), streamed.length, streamingTrue, batchTrue, mismatches, parity ? "YES" : "NO");
+ return new String[]{query, String.valueOf(corpus.size()), String.valueOf(streamingTrue),
+ String.valueOf(batchTrue), String.valueOf(mismatches), parity ? "exact" : "MISMATCH"};
+ }
+
+ private static void MeosWiringInit() {
+ org.mobilitydb.flink.meos.wirings.MeosWiringRuntime.ensureInitializedOnThread();
+ }
+
+ /** Records each continuous-form output's {@code near} flag in arrival order. */
+ private static final class CollectSink extends RichSinkFunction> {
+ @Override public void open(Configuration cfg) { }
+ @Override public void invoke(Tuple3 v, Context context) {
+ STREAMED.add(v.f2);
+ }
+ }
+}
diff --git a/flink-processor/src/main/java/berlinmod/BerlinMODQ1LocalTest.java b/flink-processor/src/main/java/berlinmod/BerlinMODQ1LocalTest.java
index 9218e16..780a4a8 100644
--- a/flink-processor/src/main/java/berlinmod/BerlinMODQ1LocalTest.java
+++ b/flink-processor/src/main/java/berlinmod/BerlinMODQ1LocalTest.java
@@ -1,3 +1,27 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
package berlinmod;
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
@@ -44,7 +68,7 @@ public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
- List events = buildEvents();
+ List events = BerlinMODCorpus.loadSample();
DataStreamSource raw = env.fromCollection(events);
DataStream trips = raw.assignTimestampsAndWatermarks(
WatermarkStrategy
@@ -70,26 +94,4 @@ public static void main(String[] args) throws Exception {
LOG.info("BerlinMODQ1LocalTest done");
}
- private static List buildEvents() {
- List events = new ArrayList<>();
- for (int i = 0; i <= 12; i += 2) {
- events.add(make(100, T0 + i * 1000L, 4.3517, 50.8503));
- }
- for (int i = 1; i <= 13; i += 2) {
- events.add(make(200, T0 + i * 1000L, 4.3060, 50.8270));
- }
- for (int i = 0; i <= 12; i += 2) {
- events.add(make(300, T0 + i * 1000L, 4.2000, 50.7500));
- }
- return events;
- }
-
- private static BerlinMODTrip make(int vid, long t, double lon, double lat) {
- BerlinMODTrip trip = new BerlinMODTrip();
- trip.setVehicleId(vid);
- trip.setTimestamp(t);
- trip.setLon(lon);
- trip.setLat(lat);
- return trip;
- }
}
diff --git a/flink-processor/src/main/java/berlinmod/BerlinMODQ2LocalTest.java b/flink-processor/src/main/java/berlinmod/BerlinMODQ2LocalTest.java
index 84950c9..74b4e8e 100644
--- a/flink-processor/src/main/java/berlinmod/BerlinMODQ2LocalTest.java
+++ b/flink-processor/src/main/java/berlinmod/BerlinMODQ2LocalTest.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
@@ -26,7 +51,7 @@
*
* - Q2-continuous: 7 events (the 7 vehicle-200 events; vehicles 100 and 300 filtered out)
* - Q2-windowed: 2 windows of size 10 s, each emitting the last vehicle-200 position seen in the window
- * - Q2-snapshot: 3 ticks × 1 emission each = 3 lines (vehicle 200's last-known position at each 5 s tick)
+ * - Q2-snapshot: 3 ticks × 1 emission each = 3 lines (vehicle 2's last-known position at each 5 s tick)
*
*
* Run after {@code mvn package} with:
@@ -38,7 +63,7 @@ public class BerlinMODQ2LocalTest {
private static final Logger LOG = LoggerFactory.getLogger(BerlinMODQ2LocalTest.class);
- private static final int TARGET_VEHICLE_ID = 200;
+ private static final int TARGET_VEHICLE_ID = 2;
private static final long WINDOW_SIZE_SECONDS = 10L;
private static final long SNAPSHOT_TICK_MILLIS = 5_000L;
private static final long T0 = 1_735_711_200_000L; // 2025-01-01 06:00:00 UTC
@@ -50,7 +75,7 @@ public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1); // deterministic output ordering
- List events = buildEvents();
+ List events = BerlinMODCorpus.loadSample();
DataStreamSource raw = env.fromCollection(events);
DataStream trips = raw.assignTimestampsAndWatermarks(
WatermarkStrategy
@@ -77,28 +102,4 @@ public static void main(String[] args) throws Exception {
LOG.info("BerlinMODQ2LocalTest done");
}
- private static List buildEvents() {
- List events = new ArrayList<>();
- // Same synthetic corpus as Q3LocalTest, so any user can run both and
- // see them work over identical inputs.
- for (int i = 0; i <= 12; i += 2) {
- events.add(make(100, T0 + i * 1000L, 4.3517, 50.8503));
- }
- for (int i = 1; i <= 13; i += 2) {
- events.add(make(200, T0 + i * 1000L, 4.3060, 50.8270));
- }
- for (int i = 0; i <= 12; i += 2) {
- events.add(make(300, T0 + i * 1000L, 4.2000, 50.7500));
- }
- return events;
- }
-
- private static BerlinMODTrip make(int vid, long t, double lon, double lat) {
- BerlinMODTrip trip = new BerlinMODTrip();
- trip.setVehicleId(vid);
- trip.setTimestamp(t);
- trip.setLon(lon);
- trip.setLat(lat);
- return trip;
- }
}
diff --git a/flink-processor/src/main/java/berlinmod/BerlinMODQ2Main.java b/flink-processor/src/main/java/berlinmod/BerlinMODQ2Main.java
index c5b9220..8919b7a 100644
--- a/flink-processor/src/main/java/berlinmod/BerlinMODQ2Main.java
+++ b/flink-processor/src/main/java/berlinmod/BerlinMODQ2Main.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.common.eventtime.SerializableTimestampAssigner;
@@ -37,9 +62,9 @@ public class BerlinMODQ2Main {
private static final Logger LOG = LoggerFactory.getLogger(BerlinMODQ2Main.class);
- // Default Q2 parameters — query vehicle 200 (Anderlecht), 10 s windows,
+ // Default Q2 parameters — query vehicle 2 (Anderlecht), 10 s windows,
// 5 s snapshot tick. Matches the synthetic-corpus defaults.
- private static final int TARGET_VEHICLE_ID = 200;
+ private static final int TARGET_VEHICLE_ID = 2;
private static final long WINDOW_SIZE_SECONDS = 10L;
private static final long SNAPSHOT_TICK_MILLIS = 5_000L;
private static final String KAFKA_TOPIC = "berlinmod";
diff --git a/flink-processor/src/main/java/berlinmod/BerlinMODQ3LocalTest.java b/flink-processor/src/main/java/berlinmod/BerlinMODQ3LocalTest.java
index 69e2022..b292136 100644
--- a/flink-processor/src/main/java/berlinmod/BerlinMODQ3LocalTest.java
+++ b/flink-processor/src/main/java/berlinmod/BerlinMODQ3LocalTest.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
@@ -26,7 +51,7 @@
*
* Synthetic corpus: 3 vehicles, 21 events over 14 simulated seconds —
*
- * - Vehicle 100 — sits on Brussels city centre {@code P}, distance 0 m, near
+ * - Vehicle 100 — sits on the canonical sample area {@code P}, distance 0 m, near
* - Vehicle 200 — Anderlecht, ~4.1 km from {@code P}, near (within the 5 km radius)
* - Vehicle 300 — Forest, ~15.4 km from {@code P}, not near (outside the 5 km radius)
*
@@ -47,8 +72,8 @@ public class BerlinMODQ3LocalTest {
private static final Logger LOG = LoggerFactory.getLogger(BerlinMODQ3LocalTest.class);
- private static final double P_LON = 4.3517;
- private static final double P_LAT = 50.8503;
+ private static final double P_LON = 4.4322; // near vehicle 1
+ private static final double P_LAT = 50.7670;
private static final double RADIUS_METRES = 5_000.0;
private static final long WINDOW_SIZE_SECONDS = 10L;
private static final long SNAPSHOT_TICK_MILLIS = 5_000L;
@@ -61,7 +86,7 @@ public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1); // deterministic output ordering for the test
- List events = buildEvents();
+ List events = BerlinMODCorpus.loadSample();
DataStreamSource raw = env.fromCollection(events);
DataStream trips = raw.assignTimestampsAndWatermarks(
WatermarkStrategy
@@ -86,29 +111,4 @@ public static void main(String[] args) throws Exception {
LOG.info("BerlinMODQ3LocalTest done");
}
- private static List buildEvents() {
- List events = new ArrayList<>();
- // Vehicle 100 — Brussels city centre (= P), 7 events at t0, t0+2s, …, t0+12s
- for (int i = 0; i <= 12; i += 2) {
- events.add(make(100, T0 + i * 1000L, 4.3517, 50.8503));
- }
- // Vehicle 200 — Anderlecht ~4.1 km from P, 7 events at t0+1s, t0+3s, …, t0+13s
- for (int i = 1; i <= 13; i += 2) {
- events.add(make(200, T0 + i * 1000L, 4.3060, 50.8270));
- }
- // Vehicle 300 — Forest ~15.4 km from P, 7 events at t0, t0+2s, …, t0+12s
- for (int i = 0; i <= 12; i += 2) {
- events.add(make(300, T0 + i * 1000L, 4.2000, 50.7500));
- }
- return events;
- }
-
- private static BerlinMODTrip make(int vid, long t, double lon, double lat) {
- BerlinMODTrip trip = new BerlinMODTrip();
- trip.setVehicleId(vid);
- trip.setTimestamp(t);
- trip.setLon(lon);
- trip.setLat(lat);
- return trip;
- }
}
diff --git a/flink-processor/src/main/java/berlinmod/BerlinMODQ3Main.java b/flink-processor/src/main/java/berlinmod/BerlinMODQ3Main.java
index 29bc518..260d874 100644
--- a/flink-processor/src/main/java/berlinmod/BerlinMODQ3Main.java
+++ b/flink-processor/src/main/java/berlinmod/BerlinMODQ3Main.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.common.eventtime.SerializableTimestampAssigner;
@@ -38,10 +63,10 @@ public class BerlinMODQ3Main {
private static final Logger LOG = LoggerFactory.getLogger(BerlinMODQ3Main.class);
- // Default Q3 parameters — Brussels city centre, 5 km radius, 10 s windows,
+ // Default Q3 parameters — the canonical sample area, 5 km radius, 10 s windows,
// 5 s snapshot tick. Matches the defaults in the spec doc.
- private static final double P_LON = 4.3517;
- private static final double P_LAT = 50.8503;
+ private static final double P_LON = 4.4322; // near canonical vehicle 1
+ private static final double P_LAT = 50.7670;
private static final double RADIUS_METRES = 5_000.0;
private static final long WINDOW_SIZE_SECONDS = 10L;
private static final long SNAPSHOT_TICK_MILLIS = 5_000L;
diff --git a/flink-processor/src/main/java/berlinmod/BerlinMODQ4LocalTest.java b/flink-processor/src/main/java/berlinmod/BerlinMODQ4LocalTest.java
index 428e7fc..11cdfc3 100644
--- a/flink-processor/src/main/java/berlinmod/BerlinMODQ4LocalTest.java
+++ b/flink-processor/src/main/java/berlinmod/BerlinMODQ4LocalTest.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
@@ -20,7 +45,7 @@
* Local end-to-end test driver for the BerlinMOD-Q4 three streaming forms.
*
* Region R = bounding box {@code (4.30, 50.84, 4.36, 50.86)} — a rectangle
- * around Brussels city centre. The synthetic corpus is designed to produce
+ * around the canonical sample area. The synthetic corpus is designed to produce
* multiple outside → inside transitions so the entry-detection logic
* is exercised non-trivially:
*
@@ -34,14 +59,14 @@
*
*
Expected output:
*
- * - Q4-continuous: 3 entries (v200's three outside → inside transitions)
+ * - Q4-continuous: 3 entries (vehicle 2's three outside → inside transitions)
* - Q4-windowed: per the intra-window scoping convention — window
- * [0, 10 s) contains v100's first-seen-inside event AND v200's two entries
- * (t=3, t=7); window [10, 20 s) contains v100's first-event-in-window
- * AND v200's third entry (t=11). 5 emissions total.
+ * [0, 10 s) contains vehicle 1's first-seen-inside event AND vehicle 2's two entries
+ * (t=3, t=7); window [10, 20 s) contains vehicle 1's first-event-in-window
+ * AND vehicle 2's third entry (t=11). 5 emissions total.
* - Q4-snapshot: cumulative entries up to each tick. Tick 5: 1
- * (v200 t=3). Tick 10: 2 (v200 t=3, t=7). Tick 15: 3 (v200 t=3, t=7,
- * t=11). v100 contributes 0 (always inside, no transition). v300
+ * (vehicle 2 t=3). Tick 10: 2 (vehicle 2 t=3, t=7). Tick 15: 3 (vehicle 2 t=3, t=7,
+ * t=11). vehicle 1 contributes 0 (always inside, no transition). vehicle 3
* contributes 0. 6 emissions total (1+2+3).
*
*/
@@ -50,9 +75,9 @@ public class BerlinMODQ4LocalTest {
private static final Logger LOG = LoggerFactory.getLogger(BerlinMODQ4LocalTest.class);
// Region R — Brussels centre rectangle
- private static final double XMIN = 4.30;
- private static final double YMIN = 50.84;
- private static final double XMAX = 4.36;
+ private static final double XMIN = 4.40;
+ private static final double YMIN = 50.74;
+ private static final double XMAX = 4.47;
private static final double YMAX = 50.86;
private static final long WINDOW_SIZE_SECONDS = 10L;
@@ -66,7 +91,7 @@ public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
- List events = buildEvents();
+ List events = BerlinMODCorpus.loadSample();
DataStreamSource raw = env.fromCollection(events);
DataStream trips = raw.assignTimestampsAndWatermarks(
WatermarkStrategy
@@ -92,39 +117,4 @@ public static void main(String[] args) throws Exception {
LOG.info("BerlinMODQ4LocalTest done");
}
- private static List buildEvents() {
- List events = new ArrayList<>();
- // v100 always inside R
- for (int i = 0; i <= 12; i += 2) {
- events.add(make(100, T0 + i * 1000L, 4.3517, 50.8503));
- }
- // v200 oscillates in/out: out, IN, out, IN, out, IN, out
- double[][] v200Path = {
- {4.3060, 50.8270}, // t=1 out (lat<50.84)
- {4.3060, 50.8500}, // t=3 IN
- {4.3060, 50.8300}, // t=5 out
- {4.3060, 50.8500}, // t=7 IN
- {4.3060, 50.8100}, // t=9 out
- {4.3060, 50.8500}, // t=11 IN
- {4.3060, 50.8300}, // t=13 out
- };
- int idx = 0;
- for (int i = 1; i <= 13; i += 2, idx++) {
- events.add(make(200, T0 + i * 1000L, v200Path[idx][0], v200Path[idx][1]));
- }
- // v300 always outside R
- for (int i = 0; i <= 12; i += 2) {
- events.add(make(300, T0 + i * 1000L, 4.2000, 50.7500));
- }
- return events;
- }
-
- private static BerlinMODTrip make(int vid, long t, double lon, double lat) {
- BerlinMODTrip trip = new BerlinMODTrip();
- trip.setVehicleId(vid);
- trip.setTimestamp(t);
- trip.setLon(lon);
- trip.setLat(lat);
- return trip;
- }
}
diff --git a/flink-processor/src/main/java/berlinmod/BerlinMODQ5LocalTest.java b/flink-processor/src/main/java/berlinmod/BerlinMODQ5LocalTest.java
index 394932d..ffb4848 100644
--- a/flink-processor/src/main/java/berlinmod/BerlinMODQ5LocalTest.java
+++ b/flink-processor/src/main/java/berlinmod/BerlinMODQ5LocalTest.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
@@ -19,23 +44,23 @@
* Local end-to-end test driver for the BerlinMOD-Q5 three streaming forms.
*
* Same stationary-vehicle corpus as Q1/Q2/Q3/Q9. Reference point P =
- * Brussels city centre (4.3517, 50.8503); {@code dP = 5 km} (vehicle near P);
+ * the canonical sample area (canonical vehicle 1); {@code dP = 5 km} (vehicle near P);
* {@code dMeet = 5 km} (pair-meeting threshold).
*
*
Pairs:
*
* - (100, 200) — both near P; distance 4.1 km ≤ dMeet → MEET
- * - (100, 300) — v300 not near P → don't qualify
- * - (200, 300) — v300 not near P → don't qualify
+ * - (100, 300) — vehicle 3 not near P → don't qualify
+ * - (200, 300) — vehicle 3 not near P → don't qualify
*
*
* Expected output (only the (100, 200) pair qualifies):
*
* - Q5-continuous: pair (100, 200) emits on every event from t=1
- * onward (the first t=0 events of v100 and v300 happen before v200 is
+ * onward (the first t=0 events of vehicle 1 and vehicle 3 happen before vehicle 2 is
* known, so no pair exists yet). 21 - 2 = 19 emissions.
* - Q5-windowed: each of the two 10-second windows contains
- * events for v100 and v200 — both qualify, the pair meets. 2 emissions.
+ * events for vehicle 1 and vehicle 2 — both qualify, the pair meets. 2 emissions.
* - Q5-snapshot: 3 ticks × 1 meeting pair = 3 emissions.
*
*/
@@ -43,10 +68,10 @@ public class BerlinMODQ5LocalTest {
private static final Logger LOG = LoggerFactory.getLogger(BerlinMODQ5LocalTest.class);
- private static final double P_LON = 4.3517;
- private static final double P_LAT = 50.8503;
+ private static final double P_LON = 4.3822; // midpoint of vehicles 1 and 2
+ private static final double P_LAT = 50.7683;
private static final double D_P_METRES = 5_000.0;
- private static final double D_MEET_METRES = 5_000.0;
+ private static final double D_MEET_METRES = 8_000.0;
private static final long WINDOW_SIZE_SECONDS = 10L;
private static final long SNAPSHOT_TICK_MILLIS = 5_000L;
private static final long T0 = 1_735_711_200_000L;
@@ -58,7 +83,7 @@ public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
- List events = buildEvents();
+ List events = BerlinMODCorpus.loadSample();
DataStreamSource raw = env.fromCollection(events);
DataStream trips = raw.assignTimestampsAndWatermarks(
WatermarkStrategy
@@ -84,26 +109,4 @@ public static void main(String[] args) throws Exception {
LOG.info("BerlinMODQ5LocalTest done");
}
- private static List buildEvents() {
- List events = new ArrayList<>();
- for (int i = 0; i <= 12; i += 2) {
- events.add(make(100, T0 + i * 1000L, 4.3517, 50.8503));
- }
- for (int i = 1; i <= 13; i += 2) {
- events.add(make(200, T0 + i * 1000L, 4.3060, 50.8270));
- }
- for (int i = 0; i <= 12; i += 2) {
- events.add(make(300, T0 + i * 1000L, 4.2000, 50.7500));
- }
- return events;
- }
-
- private static BerlinMODTrip make(int vid, long t, double lon, double lat) {
- BerlinMODTrip trip = new BerlinMODTrip();
- trip.setVehicleId(vid);
- trip.setTimestamp(t);
- trip.setLon(lon);
- trip.setLat(lat);
- return trip;
- }
}
diff --git a/flink-processor/src/main/java/berlinmod/BerlinMODQ6LocalTest.java b/flink-processor/src/main/java/berlinmod/BerlinMODQ6LocalTest.java
index 6a6fd2b..025ac07 100644
--- a/flink-processor/src/main/java/berlinmod/BerlinMODQ6LocalTest.java
+++ b/flink-processor/src/main/java/berlinmod/BerlinMODQ6LocalTest.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
@@ -33,9 +58,9 @@
* approximately:
*
*
- * - v100: 6 × 100 m = 600 m
- * - v200: 6 × 50 m = 300 m
- * - v300: 6 × 200 m = 1200 m
+ * - vehicle 1: 6 × 100 m = 600 m
+ * - vehicle 2: 6 × 50 m = 300 m
+ * - vehicle 3: 6 × 200 m = 1200 m
*
*
* Expected output:
@@ -66,7 +91,7 @@ public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
- List events = buildEvents();
+ List events = BerlinMODCorpus.loadSample();
DataStreamSource raw = env.fromCollection(events);
DataStream trips = raw.assignTimestampsAndWatermarks(
WatermarkStrategy
@@ -93,29 +118,4 @@ public static void main(String[] args) throws Exception {
LOG.info("BerlinMODQ6LocalTest done");
}
- private static List buildEvents() {
- List events = new ArrayList<>();
- int step = 0;
- for (int i = 0; i <= 12; i += 2, step++) {
- events.add(make(100, T0 + i * 1000L, 4.3517 + step * V100_DLON, 50.8503));
- }
- step = 0;
- for (int i = 1; i <= 13; i += 2, step++) {
- events.add(make(200, T0 + i * 1000L, 4.3060, 50.8270 + step * V200_DLAT));
- }
- step = 0;
- for (int i = 0; i <= 12; i += 2, step++) {
- events.add(make(300, T0 + i * 1000L, 4.2000 + step * V300_DLON, 50.7500));
- }
- return events;
- }
-
- private static BerlinMODTrip make(int vid, long t, double lon, double lat) {
- BerlinMODTrip trip = new BerlinMODTrip();
- trip.setVehicleId(vid);
- trip.setTimestamp(t);
- trip.setLon(lon);
- trip.setLat(lat);
- return trip;
- }
}
diff --git a/flink-processor/src/main/java/berlinmod/BerlinMODQ7LocalTest.java b/flink-processor/src/main/java/berlinmod/BerlinMODQ7LocalTest.java
index e7560c8..17618d2 100644
--- a/flink-processor/src/main/java/berlinmod/BerlinMODQ7LocalTest.java
+++ b/flink-processor/src/main/java/berlinmod/BerlinMODQ7LocalTest.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
@@ -22,22 +47,22 @@
*
* Same stationary-vehicle corpus as Q1/Q2/Q3/Q5/Q9. POI list:
*
- * - POI 1 = Brussels city centre (4.3517, 50.8503), radius 2000 m
- * - POI 2 = Anderlecht (4.3060, 50.8270), radius 1000 m
- * - POI 3 = south of Brussels (4.2100, 50.7600), radius 2000 m
+ * - POI 1 = the canonical sample area (canonical vehicle 1), radius 2000 m
+ * - POI 2 = Anderlecht (canonical vehicle 2), radius 1000 m
+ * - POI 3 = south of Brussels (canonical vehicle 3), radius 2000 m
*
*
* Per (vehicle, POI) match-up:
*
- * - v100 is inside POI 1 (0 m), outside POI 2 (~4.1 km) and POI 3 (~13 km)
- * - v200 is inside POI 2 (0 m), outside POI 1 and POI 3
- * - v300 is inside POI 3 (~1.3 km), outside POI 1 and POI 2
+ * - vehicle 1 is inside POI 1 (0 m), outside POI 2 (~4.1 km) and POI 3 (~13 km)
+ * - vehicle 2 is inside POI 2 (0 m), outside POI 1 and POI 3
+ * - vehicle 3 is inside POI 3 (~1.3 km), outside POI 1 and POI 2
*
*
* Expected output:
*
* - Q7-continuous: 3 emissions — first-passages on each vehicle's
- * very first event (v100 t=0 → POI 1; v200 t=1 → POI 2; v300 t=0 →
+ * very first event (vehicle 1 t=0 → POI 1; vehicle 2 t=1 → POI 2; vehicle 3 t=0 →
* POI 3)
* - Q7-windowed: per-window intra-window first-passages —
* window [0, 10 s) sees all 3 first-passages; window [10, 20 s) sees
@@ -54,9 +79,9 @@ public class BerlinMODQ7LocalTest {
private static final long T0 = 1_735_711_200_000L;
private static final List POIS = Arrays.asList(
- new PointOfInterest(1, 4.3517, 50.8503, 2_000.0),
- new PointOfInterest(2, 4.3060, 50.8270, 1_000.0),
- new PointOfInterest(3, 4.2100, 50.7600, 2_000.0));
+ new PointOfInterest(1, 4.3321, 50.7696, 2_000.0), // near vehicle 2
+ new PointOfInterest(2, 4.4571, 50.8515, 2_000.0), // near vehicle 3
+ new PointOfInterest(3, 4.4252, 50.9190, 2_000.0)); // near vehicle 5
public static void main(String[] args) throws Exception {
LOG.info("BerlinMODQ7LocalTest starting; #POIs={} window={}s tick={}ms",
@@ -65,7 +90,7 @@ public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
- List events = buildEvents();
+ List events = BerlinMODCorpus.loadSample();
DataStreamSource raw = env.fromCollection(events);
DataStream trips = raw.assignTimestampsAndWatermarks(
WatermarkStrategy
@@ -91,26 +116,4 @@ public static void main(String[] args) throws Exception {
LOG.info("BerlinMODQ7LocalTest done");
}
- private static List buildEvents() {
- List events = new ArrayList<>();
- for (int i = 0; i <= 12; i += 2) {
- events.add(make(100, T0 + i * 1000L, 4.3517, 50.8503));
- }
- for (int i = 1; i <= 13; i += 2) {
- events.add(make(200, T0 + i * 1000L, 4.3060, 50.8270));
- }
- for (int i = 0; i <= 12; i += 2) {
- events.add(make(300, T0 + i * 1000L, 4.2000, 50.7500));
- }
- return events;
- }
-
- private static BerlinMODTrip make(int vid, long t, double lon, double lat) {
- BerlinMODTrip trip = new BerlinMODTrip();
- trip.setVehicleId(vid);
- trip.setTimestamp(t);
- trip.setLon(lon);
- trip.setLat(lat);
- return trip;
- }
}
diff --git a/flink-processor/src/main/java/berlinmod/BerlinMODQ8LocalTest.java b/flink-processor/src/main/java/berlinmod/BerlinMODQ8LocalTest.java
index 9dc6709..9805cd9 100644
--- a/flink-processor/src/main/java/berlinmod/BerlinMODQ8LocalTest.java
+++ b/flink-processor/src/main/java/berlinmod/BerlinMODQ8LocalTest.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
@@ -23,14 +48,14 @@
* centre region. With a {@code d = 5 km} proximity threshold:
*
*
- * - v100 at (4.3517, 50.8503) — ~1.1 km from segment → near
- * - v200 at (4.3060, 50.8270) — ~0.5 km from segment → near
- * - v300 at (4.2000, 50.7500) — ~13 km from segment → not near
+ * - vehicle 1 at (canonical vehicle 1) — ~1.1 km from segment → near
+ * - vehicle 2 at (canonical vehicle 2) — ~0.5 km from segment → near
+ * - vehicle 3 at (canonical vehicle 3) — ~13 km from segment → not near
*
*
* Expected output shape:
*
- * - Q8-continuous: 21 events (14 near=true for v100/v200, 7 near=false for v300)
+ * - Q8-continuous: 21 events (14 near=true for vehicle 1/vehicle 2, 7 near=false for vehicle 3)
* - Q8-windowed: 2 windows, each with {@code distinctCount=2} (vehicles 100 and 200)
* - Q8-snapshot: 3 ticks × 2 near vehicles = 6 emissions
*
@@ -43,8 +68,8 @@ public class BerlinMODQ8LocalTest {
private static final Logger LOG = LoggerFactory.getLogger(BerlinMODQ8LocalTest.class);
// Road segment endpoints
- private static final double S1_LON = 4.30, S1_LAT = 50.83;
- private static final double S2_LON = 4.36, S2_LAT = 50.87;
+ private static final double S1_LON = 4.3321, S1_LAT = 50.7696; // vehicle 2
+ private static final double S2_LON = 4.3063, S2_LAT = 50.8825; // vehicle 4
private static final double RADIUS_METRES = 5_000.0;
private static final long WINDOW_SIZE_SECONDS = 10L;
private static final long SNAPSHOT_TICK_MILLIS = 5_000L;
@@ -57,7 +82,7 @@ public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
- List events = buildEvents();
+ List events = BerlinMODCorpus.loadSample();
DataStreamSource raw = env.fromCollection(events);
DataStream trips = raw.assignTimestampsAndWatermarks(
WatermarkStrategy
@@ -82,26 +107,4 @@ public static void main(String[] args) throws Exception {
LOG.info("BerlinMODQ8LocalTest done");
}
- private static List buildEvents() {
- List events = new ArrayList<>();
- for (int i = 0; i <= 12; i += 2) {
- events.add(make(100, T0 + i * 1000L, 4.3517, 50.8503));
- }
- for (int i = 1; i <= 13; i += 2) {
- events.add(make(200, T0 + i * 1000L, 4.3060, 50.8270));
- }
- for (int i = 0; i <= 12; i += 2) {
- events.add(make(300, T0 + i * 1000L, 4.2000, 50.7500));
- }
- return events;
- }
-
- private static BerlinMODTrip make(int vid, long t, double lon, double lat) {
- BerlinMODTrip trip = new BerlinMODTrip();
- trip.setVehicleId(vid);
- trip.setTimestamp(t);
- trip.setLon(lon);
- trip.setLat(lat);
- return trip;
- }
}
diff --git a/flink-processor/src/main/java/berlinmod/BerlinMODQ9LocalTest.java b/flink-processor/src/main/java/berlinmod/BerlinMODQ9LocalTest.java
index f990031..c473732 100644
--- a/flink-processor/src/main/java/berlinmod/BerlinMODQ9LocalTest.java
+++ b/flink-processor/src/main/java/berlinmod/BerlinMODQ9LocalTest.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
@@ -19,14 +44,14 @@
* Local end-to-end test driver for the BerlinMOD-Q9 three streaming forms.
*
* Same stationary-vehicle synthetic corpus as Q1/Q2/Q3 (3 vehicles, 21
- * events). Queried pair X = 100 (Brussels city centre), Y = 200 (Anderlecht);
+ * events). Queried pair X = 100 (the canonical sample area), Y = 200 (Anderlecht);
* their actual distance is ~4.1 km — the expected output for every emission.
*
*
Expected output:
*
* - Q9-continuous: 13 lines — emitted whenever either X or Y has
- * a new event AND the other has been seen at least once. v100 fires
- * first at t=0; v200's first event at t=1 produces the first paired
+ * a new event AND the other has been seen at least once. vehicle 1 fires
+ * first at t=0; vehicle 2's first event at t=1 produces the first paired
* emission; subsequent 12 events (alternating) each produce one
* emission.
* - Q9-windowed: 2 windows — both contain X and Y events, each
@@ -38,8 +63,8 @@ public class BerlinMODQ9LocalTest {
private static final Logger LOG = LoggerFactory.getLogger(BerlinMODQ9LocalTest.class);
- private static final int X_VEHICLE_ID = 100;
- private static final int Y_VEHICLE_ID = 200;
+ private static final int X_VEHICLE_ID = 1;
+ private static final int Y_VEHICLE_ID = 2;
private static final long WINDOW_SIZE_SECONDS = 10L;
private static final long SNAPSHOT_TICK_MILLIS = 5_000L;
private static final long T0 = 1_735_711_200_000L;
@@ -51,7 +76,7 @@ public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
- List events = buildEvents();
+ List events = BerlinMODCorpus.loadSample();
DataStreamSource raw = env.fromCollection(events);
DataStream trips = raw.assignTimestampsAndWatermarks(
WatermarkStrategy
@@ -82,26 +107,4 @@ public static void main(String[] args) throws Exception {
LOG.info("BerlinMODQ9LocalTest done");
}
- private static List buildEvents() {
- List events = new ArrayList<>();
- for (int i = 0; i <= 12; i += 2) {
- events.add(make(100, T0 + i * 1000L, 4.3517, 50.8503));
- }
- for (int i = 1; i <= 13; i += 2) {
- events.add(make(200, T0 + i * 1000L, 4.3060, 50.8270));
- }
- for (int i = 0; i <= 12; i += 2) {
- events.add(make(300, T0 + i * 1000L, 4.2000, 50.7500));
- }
- return events;
- }
-
- private static BerlinMODTrip make(int vid, long t, double lon, double lat) {
- BerlinMODTrip trip = new BerlinMODTrip();
- trip.setVehicleId(vid);
- trip.setTimestamp(t);
- trip.setLon(lon);
- trip.setLat(lat);
- return trip;
- }
}
diff --git a/flink-processor/src/main/java/berlinmod/BerlinMODTrip.java b/flink-processor/src/main/java/berlinmod/BerlinMODTrip.java
index 6eb8e80..1f65487 100644
--- a/flink-processor/src/main/java/berlinmod/BerlinMODTrip.java
+++ b/flink-processor/src/main/java/berlinmod/BerlinMODTrip.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
/**
diff --git a/flink-processor/src/main/java/berlinmod/MEOSBridge.java b/flink-processor/src/main/java/berlinmod/MEOSBridge.java
new file mode 100644
index 0000000..c74480f
--- /dev/null
+++ b/flink-processor/src/main/java/berlinmod/MEOSBridge.java
@@ -0,0 +1,158 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
+package berlinmod;
+
+import functions.GeneratedFunctions;
+import jnr.ffi.Pointer;
+import org.mobilitydb.flink.meos.wirings.MeosWiringRuntime;
+
+/**
+ * Thin wiring from the BerlinMOD streaming-form predicates to MEOS via JMEOS.
+ *
+ *
Every spatial predicate exercised by the BerlinMOD-9 × 3-form scaffold
+ * flows through this class, and every predicate evaluates through MEOS. The
+ * within-distance predicate is the canonical temporal operator
+ * {@code edwithin_tgeo_geo} — ever-within between the vehicle's {@code tgeogpoint}
+ * instant and the query geography, in metres on the WGS84 spheroid — the same
+ * MEOS operator the streaming-form parity contract names. Distances are
+ * {@code geog_distance} over WGS84 geographies. This class holds no spatial
+ * mathematics of its own: it constructs the MEOS inputs and delegates the
+ * computation to libmeos.
+ *
+ *
{@link MeosWiringRuntime#ensureInitializedOnThread()} initialises MEOS on
+ * the calling task thread (idempotent per thread) before the first call, since
+ * MEOS keeps its session state per OS thread.
+ */
+public final class MEOSBridge {
+
+ private MEOSBridge() {
+ // utility
+ }
+
+ // ----------------------------------------------------------------------
+ // Public predicate surface — all evaluation delegated to MEOS.
+ // ----------------------------------------------------------------------
+
+ /**
+ * @return {@code true} iff the WGS84 spheroidal distance from
+ * {@code (lon1, lat1)} to {@code (lon2, lat2)} is at most
+ * {@code radiusMetres}, via MEOS {@code edwithin_tgeo_geo} between
+ * the {@code (lon1, lat1)} {@code tgeogpoint} instant and the
+ * {@code (lon2, lat2)} point geography.
+ */
+ public static boolean dwithinMetres(double lon1, double lat1,
+ double lon2, double lat2,
+ double radiusMetres) {
+ MeosWiringRuntime.ensureInitializedOnThread();
+ return GeneratedFunctions.edwithin_tgeo_geo(
+ tgeogInst(lon1, lat1), pointGeog(lon2, lat2), radiusMetres) == 1;
+ }
+
+ /**
+ * @return {@code true} iff the WGS84 spheroidal distance from
+ * {@code (pLon, pLat)} to the LineString {@code (s1, s2)} is at most
+ * {@code radiusMetres}, via MEOS {@code edwithin_tgeo_geo} between
+ * the point {@code tgeogpoint} instant and the line geography.
+ */
+ public static boolean dwithinSegmentMetres(double pLon, double pLat,
+ double s1Lon, double s1Lat,
+ double s2Lon, double s2Lat,
+ double radiusMetres) {
+ MeosWiringRuntime.ensureInitializedOnThread();
+ return GeneratedFunctions.edwithin_tgeo_geo(
+ tgeogInst(pLon, pLat),
+ lineGeog(s1Lon, s1Lat, s2Lon, s2Lat), radiusMetres) == 1;
+ }
+
+ /**
+ * @return {@code true} iff {@code (lon, lat)} lies in the axis-aligned box
+ * {@code [xmin, xmax] × [ymin, ymax]}, via MEOS
+ * {@code eintersects_tgeo_geo} between the point's {@code tgeompoint}
+ * instant and the box polygon (planar, SRID 4326).
+ */
+ public static boolean intersectsBox(double lon, double lat,
+ double xmin, double ymin,
+ double xmax, double ymax) {
+ MeosWiringRuntime.ensureInitializedOnThread();
+ return GeneratedFunctions.eintersects_tgeo_geo(
+ tgeomInst(lon, lat), boxPolygon(xmin, ymin, xmax, ymax)) == 1;
+ }
+
+ /**
+ * @return the WGS84 spheroidal distance in metres between two points, via
+ * MEOS {@code geog_distance}.
+ */
+ public static double distanceMetres(double lon1, double lat1,
+ double lon2, double lat2) {
+ MeosWiringRuntime.ensureInitializedOnThread();
+ return GeneratedFunctions.geog_distance(pointGeog(lon1, lat1), pointGeog(lon2, lat2));
+ }
+
+ /**
+ * @return the WGS84 spheroidal distance in metres from {@code (pLon, pLat)}
+ * to the LineString {@code (s1, s2)}, via MEOS {@code geog_distance}.
+ */
+ public static double distanceSegmentMetres(double pLon, double pLat,
+ double s1Lon, double s1Lat,
+ double s2Lon, double s2Lat) {
+ MeosWiringRuntime.ensureInitializedOnThread();
+ return GeneratedFunctions.geog_distance(
+ pointGeog(pLon, pLat), lineGeog(s1Lon, s1Lat, s2Lon, s2Lat));
+ }
+
+ // ----------------------------------------------------------------------
+ // Internal helpers — construct the MEOS temporal / geography inputs.
+ // ----------------------------------------------------------------------
+
+ private static Pointer tgeogInst(double lon, double lat) {
+ return GeneratedFunctions.tgeogpoint_in(
+ String.format("SRID=4326;Point(%.7f %.7f)@2000-01-01", lon, lat));
+ }
+
+ private static Pointer tgeomInst(double lon, double lat) {
+ return GeneratedFunctions.tgeompoint_in(
+ String.format("SRID=4326;Point(%.7f %.7f)@2000-01-01", lon, lat));
+ }
+
+ private static Pointer boxPolygon(double xmin, double ymin,
+ double xmax, double ymax) {
+ return GeneratedFunctions.geom_in(String.format(
+ "SRID=4326;Polygon((%.7f %.7f, %.7f %.7f, %.7f %.7f, %.7f %.7f, %.7f %.7f))",
+ xmin, ymin, xmax, ymin, xmax, ymax, xmin, ymax, xmin, ymin), -1);
+ }
+
+ private static Pointer pointGeog(double lon, double lat) {
+ return GeneratedFunctions.geom_to_geog(
+ GeneratedFunctions.geom_in(String.format("SRID=4326;Point(%.7f %.7f)", lon, lat), -1));
+ }
+
+ private static Pointer lineGeog(double s1Lon, double s1Lat,
+ double s2Lon, double s2Lat) {
+ return GeneratedFunctions.geom_to_geog(
+ GeneratedFunctions.geom_in(String.format(
+ "SRID=4326;LineString(%.7f %.7f, %.7f %.7f)", s1Lon, s1Lat, s2Lon, s2Lat), -1));
+ }
+}
diff --git a/flink-processor/src/main/java/berlinmod/PointOfInterest.java b/flink-processor/src/main/java/berlinmod/PointOfInterest.java
index 0dc3ac5..251050f 100644
--- a/flink-processor/src/main/java/berlinmod/PointOfInterest.java
+++ b/flink-processor/src/main/java/berlinmod/PointOfInterest.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import java.io.Serializable;
diff --git a/flink-processor/src/main/java/berlinmod/Q1ContinuousFunction.java b/flink-processor/src/main/java/berlinmod/Q1ContinuousFunction.java
index dcaa383..e4476f2 100644
--- a/flink-processor/src/main/java/berlinmod/Q1ContinuousFunction.java
+++ b/flink-processor/src/main/java/berlinmod/Q1ContinuousFunction.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.common.state.ValueState;
diff --git a/flink-processor/src/main/java/berlinmod/Q1SnapshotFunction.java b/flink-processor/src/main/java/berlinmod/Q1SnapshotFunction.java
index 03c171a..7e46b96 100644
--- a/flink-processor/src/main/java/berlinmod/Q1SnapshotFunction.java
+++ b/flink-processor/src/main/java/berlinmod/Q1SnapshotFunction.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.common.state.ValueState;
diff --git a/flink-processor/src/main/java/berlinmod/Q1WindowedFunction.java b/flink-processor/src/main/java/berlinmod/Q1WindowedFunction.java
index 4581422..d06bc97 100644
--- a/flink-processor/src/main/java/berlinmod/Q1WindowedFunction.java
+++ b/flink-processor/src/main/java/berlinmod/Q1WindowedFunction.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.java.tuple.Tuple3;
diff --git a/flink-processor/src/main/java/berlinmod/Q2ContinuousFunction.java b/flink-processor/src/main/java/berlinmod/Q2ContinuousFunction.java
index 9d87dcb..fa5621f 100644
--- a/flink-processor/src/main/java/berlinmod/Q2ContinuousFunction.java
+++ b/flink-processor/src/main/java/berlinmod/Q2ContinuousFunction.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.streaming.api.functions.ProcessFunction;
diff --git a/flink-processor/src/main/java/berlinmod/Q2SnapshotFunction.java b/flink-processor/src/main/java/berlinmod/Q2SnapshotFunction.java
index 4468d82..c1e4dd3 100644
--- a/flink-processor/src/main/java/berlinmod/Q2SnapshotFunction.java
+++ b/flink-processor/src/main/java/berlinmod/Q2SnapshotFunction.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.common.state.ValueState;
diff --git a/flink-processor/src/main/java/berlinmod/Q2WindowedFunction.java b/flink-processor/src/main/java/berlinmod/Q2WindowedFunction.java
index 38ee0ff..a9dc4dd 100644
--- a/flink-processor/src/main/java/berlinmod/Q2WindowedFunction.java
+++ b/flink-processor/src/main/java/berlinmod/Q2WindowedFunction.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.java.tuple.Tuple5;
diff --git a/flink-processor/src/main/java/berlinmod/Q3ContinuousFunction.java b/flink-processor/src/main/java/berlinmod/Q3ContinuousFunction.java
index af06130..8dcad66 100644
--- a/flink-processor/src/main/java/berlinmod/Q3ContinuousFunction.java
+++ b/flink-processor/src/main/java/berlinmod/Q3ContinuousFunction.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.java.tuple.Tuple3;
@@ -16,9 +41,8 @@
* predicate and emit {@code (vehicleId, eventTimeMillis, isNear)} per event.
* No windowing — output updates per event, watermark-independent.
*
- *
Predicate today: pure-Java great-circle distance (see {@link Haversine}).
- * TODO(meos): replace with the MEOS {@code edwithin_tgeo_geo} operator via
- * JMEOS once that call is wired through (same predicate semantics, native MEOS).
+ *
Predicate: {@link MEOSBridge#dwithinMetres} — MEOS
+ * {@code edwithin_tgeo_geo} over WGS84 geographies.
*/
public class Q3ContinuousFunction extends ProcessFunction> {
@@ -39,7 +63,7 @@ public void processElement(
BerlinMODTrip trip,
Context ctx,
Collector> out) {
- boolean near = Haversine.withinMetres(trip.getLon(), trip.getLat(), pLon, pLat, radiusMetres);
+ boolean near = MEOSBridge.dwithinMetres(trip.getLon(), trip.getLat(), pLon, pLat, radiusMetres);
out.collect(new Tuple3<>(trip.getVehicleId(), trip.getTimestamp(), near));
if (LOG.isDebugEnabled()) {
LOG.debug("Q3-continuous: vehicle={} ts={} near={}", trip.getVehicleId(), trip.getTimestamp(), near);
diff --git a/flink-processor/src/main/java/berlinmod/Q3SnapshotFunction.java b/flink-processor/src/main/java/berlinmod/Q3SnapshotFunction.java
index e2340ca..f9f57ce 100644
--- a/flink-processor/src/main/java/berlinmod/Q3SnapshotFunction.java
+++ b/flink-processor/src/main/java/berlinmod/Q3SnapshotFunction.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.common.state.ValueState;
@@ -27,10 +52,9 @@
* {@code (T, vehicleId)} if the vehicle is within {@code d} of P at that
* snapshot.
*
- * Predicate today: pure-Java great-circle distance (see {@link Haversine}).
- * TODO(meos): replace with the MEOS {@code edwithin_tgeo_geo} operator via
- * JMEOS for native streaming-snapshot semantics that match the batch
- * BerlinMOD-Q3 byte-for-byte.
+ *
Predicate: {@link MEOSBridge#dwithinMetres} — MEOS
+ * {@code edwithin_tgeo_geo} over WGS84 geographies. The snapshot-form output
+ * at watermark T is equal to the batch BerlinMOD-Q3 result up to T.
*/
public class Q3SnapshotFunction
extends KeyedProcessFunction> {
@@ -80,7 +104,7 @@ public void onTimer(
if (p == null) {
return;
}
- if (Haversine.withinMetres(p.f0, p.f1, pLon, pLat, radiusMetres)) {
+ if (MEOSBridge.dwithinMetres(p.f0, p.f1, pLon, pLat, radiusMetres)) {
Integer vehicleId = ctx.getCurrentKey();
out.collect(new Tuple2<>(timestamp, vehicleId));
if (LOG.isDebugEnabled()) {
diff --git a/flink-processor/src/main/java/berlinmod/Q3WindowedFunction.java b/flink-processor/src/main/java/berlinmod/Q3WindowedFunction.java
index c4490ee..bac04e1 100644
--- a/flink-processor/src/main/java/berlinmod/Q3WindowedFunction.java
+++ b/flink-processor/src/main/java/berlinmod/Q3WindowedFunction.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.java.tuple.Tuple3;
@@ -21,9 +46,8 @@
* for which at least one event satisfies the radius predicate, and emit
* {@code (windowStart, windowEnd, distinctCount)}.
*
- * Predicate today: pure-Java great-circle distance (see {@link Haversine}).
- * TODO(meos): replace with the MEOS {@code edwithin_tgeo_geo} operator via
- * JMEOS once that call is wired through.
+ *
Predicate: {@link MEOSBridge#dwithinMetres} — MEOS
+ * {@code edwithin_tgeo_geo} over WGS84 geographies.
*/
public class Q3WindowedFunction
extends ProcessAllWindowFunction, TimeWindow> {
@@ -47,7 +71,7 @@ public void process(
Collector> out) {
Set distinctNear = new HashSet<>();
for (BerlinMODTrip trip : elements) {
- if (Haversine.withinMetres(trip.getLon(), trip.getLat(), pLon, pLat, radiusMetres)) {
+ if (MEOSBridge.dwithinMetres(trip.getLon(), trip.getLat(), pLon, pLat, radiusMetres)) {
distinctNear.add(trip.getVehicleId());
}
}
diff --git a/flink-processor/src/main/java/berlinmod/Q4ContinuousFunction.java b/flink-processor/src/main/java/berlinmod/Q4ContinuousFunction.java
index a04b804..9fd23e2 100644
--- a/flink-processor/src/main/java/berlinmod/Q4ContinuousFunction.java
+++ b/flink-processor/src/main/java/berlinmod/Q4ContinuousFunction.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.common.state.ValueState;
@@ -18,9 +43,9 @@
* inside-or-outside, and if the transition is outside→inside, emits
* {@code (vehicleId, entryTime)}.
*
- * Predicate today: pure-Java point-in-box. TODO(meos): replace with the
- * MEOS {@code eintersects_tgeo_geo} operator via the JMEOS bridge of an
- * STBox containment test for a generic polygon-R variant.
+ *
Predicate: {@link MEOSBridge#intersectsBox} — MEOS
+ * {@code eintersects_tgeo_geo} between the point's {@code tgeompoint} instant
+ * and the region polygon.
*/
public class Q4ContinuousFunction
extends KeyedProcessFunction> {
@@ -56,6 +81,6 @@ public void processElement(
}
private boolean inBox(double lon, double lat) {
- return lon >= xmin && lon <= xmax && lat >= ymin && lat <= ymax;
+ return MEOSBridge.intersectsBox(lon, lat, xmin, ymin, xmax, ymax);
}
}
diff --git a/flink-processor/src/main/java/berlinmod/Q4SnapshotFunction.java b/flink-processor/src/main/java/berlinmod/Q4SnapshotFunction.java
index 556e564..cf91444 100644
--- a/flink-processor/src/main/java/berlinmod/Q4SnapshotFunction.java
+++ b/flink-processor/src/main/java/berlinmod/Q4SnapshotFunction.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.common.state.ListState;
@@ -77,6 +102,6 @@ public void onTimer(
}
private boolean inBox(double lon, double lat) {
- return lon >= xmin && lon <= xmax && lat >= ymin && lat <= ymax;
+ return MEOSBridge.intersectsBox(lon, lat, xmin, ymin, xmax, ymax);
}
}
diff --git a/flink-processor/src/main/java/berlinmod/Q4WindowedFunction.java b/flink-processor/src/main/java/berlinmod/Q4WindowedFunction.java
index 360d589..9f70036 100644
--- a/flink-processor/src/main/java/berlinmod/Q4WindowedFunction.java
+++ b/flink-processor/src/main/java/berlinmod/Q4WindowedFunction.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.java.tuple.Tuple4;
@@ -69,6 +94,6 @@ public void process(
}
private boolean inBox(double lon, double lat) {
- return lon >= xmin && lon <= xmax && lat >= ymin && lat <= ymax;
+ return MEOSBridge.intersectsBox(lon, lat, xmin, ymin, xmax, ymax);
}
}
diff --git a/flink-processor/src/main/java/berlinmod/Q5ContinuousFunction.java b/flink-processor/src/main/java/berlinmod/Q5ContinuousFunction.java
index 93ab75b..a38a624 100644
--- a/flink-processor/src/main/java/berlinmod/Q5ContinuousFunction.java
+++ b/flink-processor/src/main/java/berlinmod/Q5ContinuousFunction.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.common.state.MapState;
@@ -31,9 +56,10 @@
* known pairs and emit {@code (a, b, eventTime, distanceMetres)} for every
* currently-meeting pair (with {@code a < b} for stable identity).
*
- * Predicate today: pure-Java great-circle distance (see {@link Haversine}).
- * TODO(meos): replace with the MEOS NAD / `edwithin_tgeo_tgeo` operator pair
- * via the JMEOS bridge.
+ *
Predicate: {@link MEOSBridge#dwithinMetres} (MEOS
+ * {@code edwithin_tgeo_geo}) for the near-P filter and
+ * {@link MEOSBridge#distanceMetres} (MEOS {@code geog_distance}) for the
+ * pairwise meeting distance.
*/
public class Q5ContinuousFunction
extends KeyedProcessFunction> {
@@ -71,7 +97,7 @@ public void processElement(
List>> nearP = new ArrayList<>();
for (Map.Entry> e : snap.entrySet()) {
Tuple2 p = e.getValue();
- if (Haversine.withinMetres(p.f0, p.f1, pLon, pLat, dPMetres)) {
+ if (MEOSBridge.dwithinMetres(p.f0, p.f1, pLon, pLat, dPMetres)) {
nearP.add(e);
}
}
@@ -81,7 +107,7 @@ public void processElement(
for (int j = i + 1; j < nearP.size(); j++) {
Tuple2 a = nearP.get(i).getValue();
Tuple2 b = nearP.get(j).getValue();
- double d = Haversine.distanceMetres(a.f0, a.f1, b.f0, b.f1);
+ double d = MEOSBridge.distanceMetres(a.f0, a.f1, b.f0, b.f1);
if (d <= dMeetMetres) {
out.collect(new Tuple4<>(
nearP.get(i).getKey(), nearP.get(j).getKey(),
diff --git a/flink-processor/src/main/java/berlinmod/Q5SnapshotFunction.java b/flink-processor/src/main/java/berlinmod/Q5SnapshotFunction.java
index 34601ef..f4f4aad 100644
--- a/flink-processor/src/main/java/berlinmod/Q5SnapshotFunction.java
+++ b/flink-processor/src/main/java/berlinmod/Q5SnapshotFunction.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.common.state.MapState;
@@ -76,7 +101,7 @@ public void onTimer(
List>> nearP = new ArrayList<>();
for (Map.Entry> e : snap.entrySet()) {
Tuple2 p = e.getValue();
- if (Haversine.withinMetres(p.f0, p.f1, pLon, pLat, dPMetres)) {
+ if (MEOSBridge.dwithinMetres(p.f0, p.f1, pLon, pLat, dPMetres)) {
nearP.add(e);
}
}
@@ -86,7 +111,7 @@ public void onTimer(
for (int j = i + 1; j < nearP.size(); j++) {
Tuple2 a = nearP.get(i).getValue();
Tuple2 b = nearP.get(j).getValue();
- double d = Haversine.distanceMetres(a.f0, a.f1, b.f0, b.f1);
+ double d = MEOSBridge.distanceMetres(a.f0, a.f1, b.f0, b.f1);
if (d <= dMeetMetres) {
out.collect(new Tuple4<>(timestamp,
nearP.get(i).getKey(), nearP.get(j).getKey(), d));
diff --git a/flink-processor/src/main/java/berlinmod/Q5WindowedFunction.java b/flink-processor/src/main/java/berlinmod/Q5WindowedFunction.java
index 1619586..ddb11e1 100644
--- a/flink-processor/src/main/java/berlinmod/Q5WindowedFunction.java
+++ b/flink-processor/src/main/java/berlinmod/Q5WindowedFunction.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.java.tuple.Tuple2;
@@ -53,7 +78,7 @@ public void process(
List>> nearP = new ArrayList<>();
for (Map.Entry e : latest.entrySet()) {
BerlinMODTrip t = e.getValue();
- if (Haversine.withinMetres(t.getLon(), t.getLat(), pLon, pLat, dPMetres)) {
+ if (MEOSBridge.dwithinMetres(t.getLon(), t.getLat(), pLon, pLat, dPMetres)) {
nearP.add(new HashMap.SimpleEntry<>(e.getKey(), new Tuple2<>(t.getLon(), t.getLat())));
}
}
@@ -63,7 +88,7 @@ public void process(
for (int j = i + 1; j < nearP.size(); j++) {
Tuple2 a = nearP.get(i).getValue();
Tuple2 b = nearP.get(j).getValue();
- double d = Haversine.distanceMetres(a.f0, a.f1, b.f0, b.f1);
+ double d = MEOSBridge.distanceMetres(a.f0, a.f1, b.f0, b.f1);
if (d <= dMeetMetres) {
out.collect(new Tuple5<>(
ctx.window().getStart(), ctx.window().getEnd(),
diff --git a/flink-processor/src/main/java/berlinmod/Q6ContinuousFunction.java b/flink-processor/src/main/java/berlinmod/Q6ContinuousFunction.java
index 0d15b22..bd47809 100644
--- a/flink-processor/src/main/java/berlinmod/Q6ContinuousFunction.java
+++ b/flink-processor/src/main/java/berlinmod/Q6ContinuousFunction.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.common.state.ValueState;
@@ -15,14 +40,12 @@
*
* "What is each vehicle's cumulative distance travelled so far?"
*
- *
Keyed by vehicleId. For each event, computes the great-circle distance
- * from the previous-known position (or 0 if first event), adds it to the
- * cumulative total, and emits {@code (vehicleId, t, cumulativeMetres)}.
+ *
Keyed by vehicleId. For each event, computes the distance from the
+ * previous-known position (or 0 if first event), adds it to the cumulative
+ * total, and emits {@code (vehicleId, t, cumulativeMetres)}.
*
- *
Predicate today: pure-Java great-circle distance (see {@link Haversine}).
- * Same MEOS-side analogue as Q3 — a future JMEOS bridge would replace the
- * Java accumulator with a MEOS {@code length} call over the per-vehicle
- * trajectory.
+ *
Distance: {@link MEOSBridge#distanceMetres} — MEOS {@code geog_distance}
+ * between consecutive WGS84 positions.
*/
public class Q6ContinuousFunction
extends KeyedProcessFunction> {
@@ -51,7 +74,7 @@ public void processElement(
total = 0.0;
}
if (prev != null) {
- total += Haversine.distanceMetres(prev.f0, prev.f1, trip.getLon(), trip.getLat());
+ total += MEOSBridge.distanceMetres(prev.f0, prev.f1, trip.getLon(), trip.getLat());
}
lastPos.update(new Tuple2<>(trip.getLon(), trip.getLat()));
totalDist.update(total);
diff --git a/flink-processor/src/main/java/berlinmod/Q6SnapshotFunction.java b/flink-processor/src/main/java/berlinmod/Q6SnapshotFunction.java
index 73117e8..4e818fb 100644
--- a/flink-processor/src/main/java/berlinmod/Q6SnapshotFunction.java
+++ b/flink-processor/src/main/java/berlinmod/Q6SnapshotFunction.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.common.state.ValueState;
@@ -55,7 +80,7 @@ public void processElement(
total = 0.0;
}
if (prev != null) {
- total += Haversine.distanceMetres(prev.f0, prev.f1, trip.getLon(), trip.getLat());
+ total += MEOSBridge.distanceMetres(prev.f0, prev.f1, trip.getLon(), trip.getLat());
}
lastPos.update(new Tuple2<>(trip.getLon(), trip.getLat()));
totalDist.update(total);
diff --git a/flink-processor/src/main/java/berlinmod/Q6WindowedFunction.java b/flink-processor/src/main/java/berlinmod/Q6WindowedFunction.java
index 4337c39..0f37f40 100644
--- a/flink-processor/src/main/java/berlinmod/Q6WindowedFunction.java
+++ b/flink-processor/src/main/java/berlinmod/Q6WindowedFunction.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.java.tuple.Tuple4;
@@ -37,7 +62,7 @@ public void process(
for (int i = 1; i < sorted.size(); i++) {
BerlinMODTrip prev = sorted.get(i - 1);
BerlinMODTrip curr = sorted.get(i);
- total += Haversine.distanceMetres(prev.getLon(), prev.getLat(),
+ total += MEOSBridge.distanceMetres(prev.getLon(), prev.getLat(),
curr.getLon(), curr.getLat());
}
out.collect(new Tuple4<>(ctx.window().getStart(), ctx.window().getEnd(), vehicleId, total));
diff --git a/flink-processor/src/main/java/berlinmod/Q7ContinuousFunction.java b/flink-processor/src/main/java/berlinmod/Q7ContinuousFunction.java
index 7a72e10..08d74cb 100644
--- a/flink-processor/src/main/java/berlinmod/Q7ContinuousFunction.java
+++ b/flink-processor/src/main/java/berlinmod/Q7ContinuousFunction.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.common.state.MapState;
@@ -48,7 +73,7 @@ public void processElement(
if (firstPassed.contains(poi.id)) {
continue;
}
- if (Haversine.withinMetres(trip.getLon(), trip.getLat(), poi.lon, poi.lat, poi.radiusMetres)) {
+ if (MEOSBridge.dwithinMetres(trip.getLon(), trip.getLat(), poi.lon, poi.lat, poi.radiusMetres)) {
firstPassed.put(poi.id, trip.getTimestamp());
out.collect(new Tuple3<>(trip.getVehicleId(), poi.id, trip.getTimestamp()));
}
diff --git a/flink-processor/src/main/java/berlinmod/Q7SnapshotFunction.java b/flink-processor/src/main/java/berlinmod/Q7SnapshotFunction.java
index 8fa1739..32a6e2f 100644
--- a/flink-processor/src/main/java/berlinmod/Q7SnapshotFunction.java
+++ b/flink-processor/src/main/java/berlinmod/Q7SnapshotFunction.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.common.state.MapState;
@@ -57,7 +82,7 @@ public void processElement(
if (firstPassed.contains(poi.id)) {
continue;
}
- if (Haversine.withinMetres(trip.getLon(), trip.getLat(),
+ if (MEOSBridge.dwithinMetres(trip.getLon(), trip.getLat(),
poi.lon, poi.lat, poi.radiusMetres)) {
firstPassed.put(poi.id, trip.getTimestamp());
}
diff --git a/flink-processor/src/main/java/berlinmod/Q7WindowedFunction.java b/flink-processor/src/main/java/berlinmod/Q7WindowedFunction.java
index 1eb9674..d273064 100644
--- a/flink-processor/src/main/java/berlinmod/Q7WindowedFunction.java
+++ b/flink-processor/src/main/java/berlinmod/Q7WindowedFunction.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.java.tuple.Tuple5;
@@ -55,7 +80,7 @@ public void process(
if (emittedPois.contains(poi.id)) {
continue;
}
- if (Haversine.withinMetres(trip.getLon(), trip.getLat(),
+ if (MEOSBridge.dwithinMetres(trip.getLon(), trip.getLat(),
poi.lon, poi.lat, poi.radiusMetres)) {
emittedPois.add(poi.id);
out.collect(new Tuple5<>(
diff --git a/flink-processor/src/main/java/berlinmod/Q8ContinuousFunction.java b/flink-processor/src/main/java/berlinmod/Q8ContinuousFunction.java
index 7eecadf..6d0a277 100644
--- a/flink-processor/src/main/java/berlinmod/Q8ContinuousFunction.java
+++ b/flink-processor/src/main/java/berlinmod/Q8ContinuousFunction.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.java.tuple.Tuple3;
@@ -15,10 +40,8 @@
* per event. No windowing — same shape as {@link Q3ContinuousFunction} but
* with a segment-distance predicate instead of a point-radius one.
*
- * Predicate today: pure-Java planar projection over an equirectangular
- * frame centred on the segment midpoint (see {@link SegmentDistance}).
- * TODO(meos): replace with the MEOS {@code distance(tgeompoint,
- * geometry(LINESTRING))} call via the JMEOS bridge.
+ *
Predicate: {@link MEOSBridge#dwithinSegmentMetres} — MEOS
+ * {@code edwithin_tgeo_geo} against a LineString geography.
*/
public class Q8ContinuousFunction extends ProcessFunction> {
@@ -39,7 +62,7 @@ public void processElement(
BerlinMODTrip trip,
Context ctx,
Collector> out) {
- boolean near = SegmentDistance.withinMetres(
+ boolean near = MEOSBridge.dwithinSegmentMetres(
trip.getLon(), trip.getLat(),
s1Lon, s1Lat, s2Lon, s2Lat,
radiusMetres);
diff --git a/flink-processor/src/main/java/berlinmod/Q8SnapshotFunction.java b/flink-processor/src/main/java/berlinmod/Q8SnapshotFunction.java
index 5198e70..034bb52 100644
--- a/flink-processor/src/main/java/berlinmod/Q8SnapshotFunction.java
+++ b/flink-processor/src/main/java/berlinmod/Q8SnapshotFunction.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.common.state.ValueState;
@@ -71,7 +96,7 @@ public void onTimer(
if (p == null) {
return;
}
- if (SegmentDistance.withinMetres(p.f0, p.f1, s1Lon, s1Lat, s2Lon, s2Lat, radiusMetres)) {
+ if (MEOSBridge.dwithinSegmentMetres(p.f0, p.f1, s1Lon, s1Lat, s2Lon, s2Lat, radiusMetres)) {
out.collect(new Tuple2<>(timestamp, ctx.getCurrentKey()));
}
}
diff --git a/flink-processor/src/main/java/berlinmod/Q8WindowedFunction.java b/flink-processor/src/main/java/berlinmod/Q8WindowedFunction.java
index ace2dce..85a1fac 100644
--- a/flink-processor/src/main/java/berlinmod/Q8WindowedFunction.java
+++ b/flink-processor/src/main/java/berlinmod/Q8WindowedFunction.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.java.tuple.Tuple3;
@@ -41,7 +66,7 @@ public void process(
Collector> out) {
Set distinctNear = new HashSet<>();
for (BerlinMODTrip trip : elements) {
- if (SegmentDistance.withinMetres(
+ if (MEOSBridge.dwithinSegmentMetres(
trip.getLon(), trip.getLat(),
s1Lon, s1Lat, s2Lon, s2Lat,
radiusMetres)) {
diff --git a/flink-processor/src/main/java/berlinmod/Q9ContinuousFunction.java b/flink-processor/src/main/java/berlinmod/Q9ContinuousFunction.java
index ae431e8..2cd4aac 100644
--- a/flink-processor/src/main/java/berlinmod/Q9ContinuousFunction.java
+++ b/flink-processor/src/main/java/berlinmod/Q9ContinuousFunction.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.common.state.ValueState;
@@ -62,7 +87,7 @@ public void processElement(
}
xy.update(s);
if (!Double.isNaN(s.f0) && !Double.isNaN(s.f2)) {
- double d = Haversine.distanceMetres(s.f0, s.f1, s.f2, s.f3);
+ double d = MEOSBridge.distanceMetres(s.f0, s.f1, s.f2, s.f3);
out.collect(new Tuple2<>(trip.getTimestamp(), d));
}
}
diff --git a/flink-processor/src/main/java/berlinmod/Q9SnapshotFunction.java b/flink-processor/src/main/java/berlinmod/Q9SnapshotFunction.java
index 6bce22b..afedd24 100644
--- a/flink-processor/src/main/java/berlinmod/Q9SnapshotFunction.java
+++ b/flink-processor/src/main/java/berlinmod/Q9SnapshotFunction.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.common.state.ValueState;
@@ -73,7 +98,7 @@ public void onTimer(
Collector> out) throws Exception {
Tuple4 s = xy.value();
if (s != null && !Double.isNaN(s.f0) && !Double.isNaN(s.f2)) {
- double d = Haversine.distanceMetres(s.f0, s.f1, s.f2, s.f3);
+ double d = MEOSBridge.distanceMetres(s.f0, s.f1, s.f2, s.f3);
out.collect(new Tuple2<>(timestamp, d));
}
}
diff --git a/flink-processor/src/main/java/berlinmod/Q9WindowedFunction.java b/flink-processor/src/main/java/berlinmod/Q9WindowedFunction.java
index 6f1cd09..bcdfa48 100644
--- a/flink-processor/src/main/java/berlinmod/Q9WindowedFunction.java
+++ b/flink-processor/src/main/java/berlinmod/Q9WindowedFunction.java
@@ -1,3 +1,28 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
package berlinmod;
import org.apache.flink.api.java.tuple.Tuple3;
@@ -45,7 +70,7 @@ public void process(
}
}
if (latestX != null && latestY != null) {
- double d = Haversine.distanceMetres(
+ double d = MEOSBridge.distanceMetres(
latestX.getLon(), latestX.getLat(),
latestY.getLon(), latestY.getLat());
out.collect(new Tuple3<>(ctx.window().getStart(), ctx.window().getEnd(), d));
diff --git a/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/MeosBoundedStateMap.java b/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/MeosBoundedStateMap.java
new file mode 100644
index 0000000..4776f11
--- /dev/null
+++ b/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/MeosBoundedStateMap.java
@@ -0,0 +1,183 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
+package org.mobilitydb.flink.meos.wirings;
+
+import jnr.ffi.Pointer;
+import org.apache.flink.api.common.state.ValueState;
+import org.apache.flink.api.common.state.ValueStateDescriptor;
+import org.apache.flink.api.common.typeinfo.PrimitiveArrayTypeInfo;
+import org.apache.flink.configuration.Configuration;
+import org.apache.flink.streaming.api.functions.KeyedProcessFunction;
+import org.apache.flink.util.Collector;
+
+import java.io.Serializable;
+
+/**
+ * DataStream wiring for the {@code bounded-state} streaming tier of
+ * the generated {@code org.mobilitydb.meos.MeosOps*} facades.
+ *
+ * The {@code bounded-state} tier is "per-event with bounded per-key
+ * state, the state IS a MEOS handle". The canonical example is a
+ * per-key accumulator that keeps the running MEOS value alive across
+ * events (e.g. per-vehicle running trajectory, per-key tbox union).
+ *
+ *
Why state lives as bytes, not as a {@code Pointer}. A
+ * {@code jnr.ffi.Pointer} is a raw native-memory address. It is not
+ * portable across JVM restarts; Flink would not be able to checkpoint
+ * or replay state. The wiring stores the state as a {@code byte[]}
+ * (typically the MEOS-WKB serialization of the temporal value), with
+ * adopter-supplied serialize / deserialize / step lambdas mediating
+ * the round-trip through MEOS:
+ *
+ *
{@code
+ * byte[] state -- the per-key serialized MEOS value
+ * ↓ deserialize (MEOS-WKB → Pointer)
+ * Pointer prev -- the in-flight MEOS handle
+ * ↓ step(prev, event) → (newPointer, output)
+ * Pointer next, OUT out -- the new in-flight handle + per-event output
+ * ↓ serialize (Pointer → MEOS-WKB)
+ * byte[] newState -- the per-key serialized new MEOS value
+ * }
+ *
+ * This serde discipline is the same one MobilityDuck's persistent
+ * state machines use; it survives Flink savepoint / checkpoint /
+ * rescaling correctly because state crossing the operator boundary is
+ * always the MEOS-WKB bytes — never a raw pointer.
+ *
+ *
Typical usage — per-vehicle running tbox union via
+ * {@code MeosOpsFreeCore.union_tbox_tbox} (stateless on its own;
+ * stateful when applied as a running fold):
+ *
+ *
{@code
+ * DataStream in = ...; // (vehicleId, tbox)
+ * DataStream out = in
+ * .keyBy(VehicleTbox::vehicleId)
+ * .process(new MeosBoundedStateMap(
+ * /* serialize *(/ ptr -> MeosOpsTBox.tbox_as_wkb(ptr, (byte) 4).array(),
+ * /* deserialize *(/ bytes -> MeosOpsTBox.tbox_from_wkb(Pointer.wrap(...), bytes.length),
+ * /* step *(/ (prev, evt) -> {
+ * Pointer eventTbox = evt.toMeosTbox();
+ * Pointer merged = (prev == null) ? eventTbox
+ * : MeosOpsFreeCore.union_tbox_tbox(prev, eventTbox);
+ * RunningTbox result = new RunningTbox(evt.vehicleId(), MeosOpsTBox.tbox_as_hexwkb(merged, (byte) 4, null));
+ * return new MeosStep<>(merged, result);
+ * }));
+ * }
+ *
+ * The first event for a key sees {@code prev == null} (no prior
+ * state); the wiring handles that case by skipping the
+ * {@code deserialize} call. On subsequent events, the state is
+ * re-hydrated, mutated, re-serialized.
+ *
+ *
Coverage: bounded-state is the second-largest tier in the
+ * v4 baseline (797 of 2,097 emitted methods — 513 OO-classified + 284
+ * free-fn). Any of them can be wrapped through this single class —
+ * adopters provide the three lambdas, the wiring handles all of the
+ * Flink state plumbing.
+ *
+ *
State serializer: this implementation uses Flink's built-in
+ * {@code byte[]} primitive-array serializer (no custom Kryo / Avro / Pojo
+ * registration needed). The state size per key is bounded by the
+ * MEOS-WKB size of the running value — sub-KB for typical
+ * accumulator scenarios.
+ *
+ * @param the key type ({@code keyBy} extractor return type)
+ * @param the input event type
+ * @param the output type emitted per event
+ */
+public final class MeosBoundedStateMap
+ extends KeyedProcessFunction {
+
+ /** Serializable Pointer → bytes serializer (typically MEOS-WKB). */
+ @FunctionalInterface
+ public interface PointerSerialize extends Serializable {
+ byte[] toBytes(Pointer pointer) throws Exception;
+ }
+
+ /** Serializable bytes → Pointer deserializer (typically MEOS-WKB). */
+ @FunctionalInterface
+ public interface PointerDeserialize extends Serializable {
+ Pointer fromBytes(byte[] bytes) throws Exception;
+ }
+
+ /** Per-event step: (prior MEOS handle, event) → (new handle, output). */
+ @FunctionalInterface
+ public interface MeosStepFn extends Serializable {
+ MeosStep apply(Pointer prior, IN event) throws Exception;
+ }
+
+ /** Tuple returned by the step lambda. */
+ public static final class MeosStep implements Serializable {
+ private static final long serialVersionUID = 1L;
+ public final Pointer newState;
+ public final OUT output;
+ public MeosStep(Pointer newState, OUT output) {
+ this.newState = newState;
+ this.output = output;
+ }
+ }
+
+ private final PointerSerialize serialize;
+ private final PointerDeserialize deserialize;
+ private final MeosStepFn step;
+
+ private transient ValueState handleState;
+
+ public MeosBoundedStateMap(PointerSerialize serialize,
+ PointerDeserialize deserialize,
+ MeosStepFn step) {
+ this.serialize = serialize;
+ this.deserialize = deserialize;
+ this.step = step;
+ }
+
+ @Override
+ public void open(Configuration parameters) throws Exception {
+ super.open(parameters);
+ MeosWiringRuntime.ensureInitializedOnThread();
+ ValueStateDescriptor descriptor = new ValueStateDescriptor<>(
+ "meos-bounded-state",
+ PrimitiveArrayTypeInfo.BYTE_PRIMITIVE_ARRAY_TYPE_INFO);
+ handleState = getRuntimeContext().getState(descriptor);
+ }
+
+ @Override
+ public void processElement(IN event,
+ KeyedProcessFunction.Context ctx,
+ Collector out) throws Exception {
+ byte[] priorBytes = handleState.value();
+ Pointer prior = (priorBytes == null) ? null : deserialize.fromBytes(priorBytes);
+
+ MeosStep stepResult = step.apply(prior, event);
+
+ byte[] newBytes = serialize.toBytes(stepResult.newState);
+ handleState.update(newBytes);
+
+ if (stepResult.output != null) {
+ out.collect(stepResult.output);
+ }
+ }
+}
diff --git a/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/MeosCrossStreamJoin.java b/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/MeosCrossStreamJoin.java
new file mode 100644
index 0000000..dfefd6a
--- /dev/null
+++ b/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/MeosCrossStreamJoin.java
@@ -0,0 +1,140 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
+package org.mobilitydb.flink.meos.wirings;
+
+import org.apache.flink.configuration.Configuration;
+import org.apache.flink.streaming.api.functions.co.ProcessJoinFunction;
+import org.apache.flink.util.Collector;
+
+import java.io.Serializable;
+
+/**
+ * DataStream wiring for the {@code cross-stream} streaming tier of
+ * the generated {@code org.mobilitydb.meos.MeosOps*} facades.
+ *
+ * The {@code cross-stream} tier is "pairwise across two streams,
+ * pre-keyed by the same K, time-bounded match window". Canonical
+ * examples are spatial-relations between two trajectories
+ * ({@code edwithin_tgeo_tgeo}, {@code eintersects_tgeo_tgeo}) and
+ * distance functions on two temporals
+ * ({@code nad_tgeo_tgeo}, {@code mindistance_tgeo_tgeo}).
+ *
+ *
Wraps any cross-stream MeosOps call as a Flink
+ * {@link ProcessJoinFunction} (the operator backing
+ * {@code KeyedStream.intervalJoin(other)}). The wiring receives one
+ * left event and one right event per match, both already paired by
+ * Flink's interval-join machinery, and the adopter's lambda computes
+ * the pairwise output via the matching MeosOps call.
+ *
+ *
Typical usage — per-vehicle-pair "did they come within
+ * 100m of each other in the last 5 minutes?" via
+ * {@code MeosOpsTGeo.edwithin_tgeo_tgeo} (tier = {@code cross-stream}):
+ *
+ *
{@code
+ * KeyedStream a = streamA.keyBy(VehiclePosition::regionId);
+ * KeyedStream b = streamB.keyBy(VehiclePosition::regionId);
+ *
+ * DataStream meetings = a
+ * .intervalJoin(b)
+ * .between(Time.minutes(-5), Time.minutes(5))
+ * .process(new MeosCrossStreamJoin(
+ * (left, right, ctx) -> {
+ * Pointer leftT = left.toTGeoPointer();
+ * Pointer rightT = right.toTGeoPointer();
+ * if (MeosOpsTGeo.edwithin_tgeo_tgeo(leftT, rightT, 100.0) != 0) {
+ * return new MeetingEvent(left.id(), right.id(), ctx.getLeftTimestamp());
+ * }
+ * return null; // no output for non-matches
+ * }));
+ * }
+ *
+ * The interval-join is keyed (both streams must be pre-keyed by
+ * the same K, and only events sharing a key are considered for
+ * pairing). The match window is time-bounded
+ * ({@code .between(lowerBound, upperBound)}) and event-time aware —
+ * watermarks drive when matches are emitted.
+ *
+ *
Slim adopter signature — same {@code ContextLike}-style
+ * pattern as {@link MeosWindowedAggregate}: the lambda receives the
+ * matched left + right events and a slim context exposing the
+ * left/right timestamps (the bits a MEOS cross-stream call typically
+ * needs), keeping the wiring lambda free of Flink internals.
+ *
+ *
Coverage: 140 of the 2,097 emitted methods (~7%) qualify
+ * as {@code cross-stream} per the v4 baseline — all of them wrappable
+ * through this single class. With this PR, every streamable tier in
+ * the baseline has a generic wiring class; 1,957 of 2,097 (93%) of
+ * the generated MeosOps* methods are wirable through 4 classes
+ * without per-method registration.
+ *
+ * @param the left-stream event type
+ * @param the right-stream event type
+ * @param the per-match output type
+ */
+public final class MeosCrossStreamJoin
+ extends ProcessJoinFunction {
+
+ /** Serializable per-match MEOS pairwise call. */
+ @FunctionalInterface
+ public interface JoinFn extends Serializable {
+ OUT join(L left, R right, ContextLike ctx) throws Exception;
+ }
+
+ /**
+ * Slimmer alternative to Flink's {@code ProcessJoinFunction.Context}
+ * — exposes only the bits a MEOS pairwise call typically needs.
+ */
+ public interface ContextLike {
+ long getLeftTimestamp();
+ long getRightTimestamp();
+ }
+
+ private final JoinFn joinFn;
+
+ public MeosCrossStreamJoin(JoinFn joinFn) {
+ this.joinFn = joinFn;
+ }
+
+ @Override
+ public void open(Configuration parameters) throws Exception {
+ super.open(parameters);
+ MeosWiringRuntime.ensureInitializedOnThread();
+ }
+
+ @Override
+ public void processElement(L left, R right,
+ ProcessJoinFunction.Context context,
+ Collector out) throws Exception {
+ ContextLike ctx = new ContextLike() {
+ @Override public long getLeftTimestamp() { return context.getLeftTimestamp(); }
+ @Override public long getRightTimestamp() { return context.getRightTimestamp(); }
+ };
+ OUT result = joinFn.join(left, right, ctx);
+ if (result != null) {
+ out.collect(result);
+ }
+ }
+}
diff --git a/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/MeosStatelessFilter.java b/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/MeosStatelessFilter.java
new file mode 100644
index 0000000..0ac8163
--- /dev/null
+++ b/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/MeosStatelessFilter.java
@@ -0,0 +1,112 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
+package org.mobilitydb.flink.meos.wirings;
+
+import org.apache.flink.api.common.functions.FilterFunction;
+import org.apache.flink.api.common.functions.RichFilterFunction;
+import org.apache.flink.configuration.Configuration;
+
+import java.io.Serializable;
+
+/**
+ * DataStream wiring for the {@code stateless} streaming tier of the
+ * generated {@code org.mobilitydb.meos.MeosOps*} facades — the
+ * predicate-shaped sibling of {@link MeosStatelessMap}.
+ *
+ * Wraps any {@code MeosOps*.f(...)} call that returns {@code boolean}
+ * (or {@code int} interpreted as a 0/1 flag, common in JMEOS' int-coded
+ * predicates) and whose streaming tier is {@code stateless} (per
+ * {@code tools/codegen/meos-ops-manifest.json}) as a Flink
+ * {@link FilterFunction}. No per-key state; each event filtered
+ * independently.
+ *
+ *
Typical usage: scalar-predicate filter against the
+ * generated {@code MeosOpsTBox.overlaps_tbox_tbox} (tier =
+ * {@code stateless}):
+ *
+ *
{@code
+ * DataStream in = ...;
+ * DataStream overlapping = in.filter(
+ * new MeosStatelessFilter<>(
+ * pair -> MeosOpsTBox.overlaps_tbox_tbox(pair.a, pair.b)));
+ * }
+ *
+ * For int-coded predicates (JMEOS returns {@code int} for some MEOS
+ * predicates rather than {@code boolean}), use
+ * {@link #fromIntPredicate}:
+ *
+ *
{@code
+ * DataStream in = ...;
+ * DataStream adj = in.filter(
+ * MeosStatelessFilter.fromIntPredicate(
+ * pair -> MeosOpsFreeGeo.adjacent_stbox_stbox(pair.a, pair.b)));
+ * }
+ *
+ * @param the record type being filtered
+ */
+public final class MeosStatelessFilter extends RichFilterFunction {
+
+ /** Serializable boolean-returning per-event MEOS predicate. */
+ @FunctionalInterface
+ public interface MeosPredicate extends Serializable {
+ boolean test(IN event) throws Exception;
+ }
+
+ /** Serializable int-returning per-event MEOS predicate (0/1 flag). */
+ @FunctionalInterface
+ public interface MeosIntPredicate extends Serializable {
+ int test(IN event) throws Exception;
+ }
+
+ private final MeosPredicate predicate;
+
+ public MeosStatelessFilter(MeosPredicate predicate) {
+ this.predicate = predicate;
+ }
+
+ /**
+ * Adapt an {@code int}-returning generated MEOS predicate (treating
+ * non-zero as {@code true}) into a Flink {@code FilterFunction}.
+ */
+ public static MeosStatelessFilter fromIntPredicate(MeosIntPredicate p) {
+ return new MeosStatelessFilter<>(event -> p.test(event) != 0);
+ }
+
+ @Override
+ public void open(Configuration parameters) throws Exception {
+ super.open(parameters);
+ MeosWiringRuntime.ensureInitializedOnThread();
+ }
+
+ @Override
+ public boolean filter(IN event) throws Exception {
+ // When chained to a legacy source, records are processed on the source's
+ // emitter thread rather than the thread open() ran on; the ThreadLocal
+ // guard makes this a cheap no-op after the first call per thread.
+ MeosWiringRuntime.ensureInitializedOnThread();
+ return predicate.test(event);
+ }
+}
diff --git a/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/MeosStatelessMap.java b/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/MeosStatelessMap.java
new file mode 100644
index 0000000..7ac2f6c
--- /dev/null
+++ b/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/MeosStatelessMap.java
@@ -0,0 +1,118 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
+package org.mobilitydb.flink.meos.wirings;
+
+import org.apache.flink.api.common.functions.MapFunction;
+import org.apache.flink.api.common.functions.RichMapFunction;
+import org.apache.flink.configuration.Configuration;
+
+import java.io.Serializable;
+
+/**
+ * DataStream wiring for the {@code stateless} streaming tier of the
+ * generated {@code org.mobilitydb.meos.MeosOps*} facades.
+ *
+ * Wraps any {@code MeosOps*.f(...)} call whose streaming tier is
+ * {@code stateless} (per {@code tools/codegen/meos-ops-manifest.json}
+ * or {@code meos-ops-free-manifest.json}) as a Flink
+ * {@link MapFunction}. No per-key state is allocated; each event is
+ * mapped independently. The wrapped call:
+ *
+ *
+ * - does no MEOS-handle state across events (per the
+ * {@code stateless} tier contract);
+ * - does not touch the time domain (no window required);
+ * - may delegate to MEOS via the bundled JMEOS jar when
+ * {@code MeosOpsRuntime.MEOS_AVAILABLE} — otherwise the
+ * generated facade throws {@code UnsupportedOperationException}.
+ *
+ *
+ * Typical usage: register a stateless MEOS predicate / arithmetic
+ * call as a per-event map step in a DataStream pipeline. Example with
+ * the generated {@code MeosOpsTBox.overlaps_tbox_tbox} (tier =
+ * {@code stateless}, per the codegen manifest):
+ *
+ *
{@code
+ * DataStream in = ...; // (tboxA, tboxB)
+ * DataStream overlap = in.map(
+ * new MeosStatelessMap<>(
+ * pair -> MeosOpsTBox.overlaps_tbox_tbox(pair.a, pair.b)));
+ * }
+ *
+ * Tier coverage: as of the codegen state on the parent PR,
+ * 804 of the 2,097 generated methods are {@code stateless} (92 OO-
+ * classified + 712 free-fn). Any of those can be wrapped through this
+ * single class without per-method boilerplate.
+ *
+ *
Coexistence with {@code berlinmod.MEOSBridge}: this is the
+ * low-level catalog-shaped wiring; {@code MEOSBridge} stays as
+ * the high-level query-shaped wiring for the BerlinMOD-9 suite.
+ * Both share the same {@code MeosOpsRuntime.MEOS_AVAILABLE} discipline.
+ *
+ * @param the input record type
+ * @param the output type returned by the wrapped MEOS call
+ */
+public final class MeosStatelessMap extends RichMapFunction {
+
+ /**
+ * Serializable per-event MEOS call. Implementations forward to a
+ * generated {@code MeosOps*.f(...)} static method, returning the
+ * Java type that the generated facade exposes.
+ */
+ @FunctionalInterface
+ public interface MeosCall extends Serializable {
+ OUT apply(IN event) throws Exception;
+ }
+
+ private final MeosCall call;
+
+ /**
+ * @param call serializable lambda forwarding to a stateless
+ * generated MEOS facade method. The lambda must be
+ * serializable (Java 8+ lambdas implementing a
+ * {@link Serializable} functional interface are).
+ */
+ public MeosStatelessMap(MeosCall call) {
+ this.call = call;
+ }
+
+ @Override
+ public void open(Configuration parameters) throws Exception {
+ super.open(parameters);
+ // No per-key state in the stateless tier; the only per-operator
+ // concern is MEOS' per-thread session, initialized on this task thread.
+ MeosWiringRuntime.ensureInitializedOnThread();
+ }
+
+ @Override
+ public OUT map(IN event) throws Exception {
+ // When chained to a legacy source, records are processed on the source's
+ // emitter thread rather than the thread open() ran on; the ThreadLocal
+ // guard makes this a cheap no-op after the first call per thread.
+ MeosWiringRuntime.ensureInitializedOnThread();
+ return call.apply(event);
+ }
+}
diff --git a/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/MeosWindowedAggregate.java b/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/MeosWindowedAggregate.java
new file mode 100644
index 0000000..7091253
--- /dev/null
+++ b/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/MeosWindowedAggregate.java
@@ -0,0 +1,148 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
+package org.mobilitydb.flink.meos.wirings;
+
+import org.apache.flink.configuration.Configuration;
+import org.apache.flink.streaming.api.functions.windowing.ProcessWindowFunction;
+import org.apache.flink.streaming.api.windowing.windows.Window;
+import org.apache.flink.util.Collector;
+
+import java.io.Serializable;
+
+/**
+ * DataStream wiring for the {@code windowed} streaming tier of the
+ * generated {@code org.mobilitydb.meos.MeosOps*} facades.
+ *
+ * The {@code windowed} tier is "output cardinality changes; needs a
+ * window". The canonical examples are
+ * {@code temporal_length(tgeo)} (one length per trajectory window),
+ * {@code temporal_twavg(tnumber)} (one time-weighted average per
+ * window), and the per-class {@code _trajectory} / {@code _time} /
+ * {@code _timespan} accessors that reduce a full sequence to a single
+ * derived value.
+ *
+ *
Wraps any windowed MeosOps call as a Flink
+ * {@link ProcessWindowFunction}: per-window, the adopter receives the
+ * full iterable of events in the window, applies whatever MEOS
+ * sequence-derived operation is appropriate, and emits a single
+ * per-window output. The wiring handles the
+ * {@code ProcessWindowFunction} boilerplate (context, collector) so
+ * adopters write a single serializable lambda.
+ *
+ *
State considerations: unlike
+ * {@link MeosBoundedStateMap}, the {@code windowed} tier does not
+ * keep MEOS handles across event boundaries — each window's MEOS
+ * value is built fresh from the iterable on window close (or
+ * watermark trigger), used to compute the output, and discarded. The
+ * iterable's events are Flink-side data; MEOS handles are short-lived
+ * per-window.
+ *
+ *
Typical usage — per-vehicle per-tumbling-window
+ * trajectory length via {@code MeosOpsTemporal.temporal_length} (tier
+ * = {@code windowed}):
+ *
+ *
{@code
+ * DataStream events = ...; // (vehicleId, lon, lat, timestamp)
+ * DataStream lengths = events
+ * .assignTimestampsAndWatermarks(...)
+ * .keyBy(VehiclePoint::vehicleId)
+ * .window(TumblingEventTimeWindows.of(Time.minutes(10)))
+ * .process(new MeosWindowedAggregate(
+ * (window, events, ctx) -> {
+ * Pointer trajectory = buildTrajectoryFromPoints(events); // adopter helper
+ * double length = MeosOpsTemporal.temporal_length(trajectory);
+ * return new VehicleLength(ctx.getCurrentKey(), window.getStart(), length);
+ * }));
+ * }
+ *
+ * The window-close path is event-time-aware: when Flink determines
+ * the window is complete (via watermark), it invokes the lambda once
+ * with the full iterable, the window metadata, and a context giving
+ * access to the key. The adopter returns a single output value.
+ *
+ *
Coverage: 161 of the 2,097 emitted methods (~8%) qualify
+ * as {@code windowed} per the v4 baseline — all of them wrappable
+ * through this single class.
+ *
+ * @param the key type
+ * @param the input event type within the window
+ * @param the per-window output type
+ * @param the window type ({@code TimeWindow}, {@code GlobalWindow}, etc.)
+ */
+public final class MeosWindowedAggregate
+ extends ProcessWindowFunction {
+
+ /**
+ * Serializable per-window MEOS aggregate. The lambda receives the
+ * window metadata, the full iterable of in-window events, and a
+ * context (for key access). It returns a single per-window output
+ * value, or {@code null} to emit nothing.
+ */
+ @FunctionalInterface
+ public interface WindowFn extends Serializable {
+ OUT aggregate(W window, Iterable events, ContextLike ctx) throws Exception;
+ }
+
+ /**
+ * Slimmer alternative to Flink's {@code ProcessWindowFunction.Context}
+ * — exposes only the bits a MEOS aggregate typically needs (key +
+ * current processing time + current watermark). Keeps the wiring
+ * lambda free of Flink internals.
+ */
+ public interface ContextLike {
+ K getCurrentKey();
+ long getCurrentProcessingTime();
+ long getCurrentWatermark();
+ }
+
+ private final WindowFn windowFn;
+
+ public MeosWindowedAggregate(WindowFn windowFn) {
+ this.windowFn = windowFn;
+ }
+
+ @Override
+ public void open(Configuration parameters) throws Exception {
+ super.open(parameters);
+ MeosWiringRuntime.ensureInitializedOnThread();
+ }
+
+ @Override
+ public void process(K key,
+ ProcessWindowFunction.Context context,
+ Iterable elements,
+ Collector out) throws Exception {
+ ContextLike ctx = new ContextLike() {
+ @Override public K getCurrentKey() { return key; }
+ @Override public long getCurrentProcessingTime() { return context.currentProcessingTime(); }
+ @Override public long getCurrentWatermark() { return context.currentWatermark(); }
+ };
+ OUT result = windowFn.aggregate(context.window(), elements, ctx);
+ if (result != null) {
+ out.collect(result);
+ }
+ }
+}
diff --git a/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/MeosWiringRuntime.java b/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/MeosWiringRuntime.java
new file mode 100644
index 0000000..c99bf16
--- /dev/null
+++ b/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/MeosWiringRuntime.java
@@ -0,0 +1,62 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
+package org.mobilitydb.flink.meos.wirings;
+
+import functions.GeneratedFunctions;
+
+/**
+ * Per-thread MEOS initialization for the {@code org.mobilitydb.flink.meos.wirings}
+ * operators.
+ *
+ * MEOS keeps its timezone / session state per OS thread. Each Flink
+ * subtask runs on its own task thread, so every wiring operator must
+ * initialize MEOS on that thread from its {@code open()} — the JVM-wide
+ * probe in {@code MeosOpsRuntime} only covers the thread that first
+ * touches a facade class (typically the job's main thread), not the task
+ * threads where the operators actually run.
+ *
+ *
{@link #ensureInitializedOnThread()} is idempotent per thread (guarded
+ * by a {@link ThreadLocal}), so it is safe to call from every operator's
+ * {@code open()} even when operators are chained onto the same thread. It
+ * installs a no-op error handler so a MEOS-side error surfaces as a thrown
+ * exception rather than terminating the JVM.
+ */
+public final class MeosWiringRuntime {
+
+ private static final ThreadLocal INITIALIZED =
+ ThreadLocal.withInitial(() -> Boolean.FALSE);
+
+ private MeosWiringRuntime() { /* utility */ }
+
+ /** Initialize MEOS on the calling thread exactly once. */
+ public static void ensureInitializedOnThread() {
+ if (!INITIALIZED.get()) {
+ GeneratedFunctions.meos_initialize_error_handler((level, code, message) -> { });
+ GeneratedFunctions.meos_initialize();
+ INITIALIZED.set(Boolean.TRUE);
+ }
+ }
+}
diff --git a/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/README.md b/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/README.md
new file mode 100644
index 0000000..b79ec19
--- /dev/null
+++ b/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/README.md
@@ -0,0 +1,89 @@
+# DataStream wirings for the generated MEOS facades
+
+This package supplies thin, generic Flink-DataStream wrappers around
+the generated `org.mobilitydb.meos.MeosOps*` facades, organized
+per **streaming tier** (per
+`tools/codegen/meos-ops-manifest.json` + `tools/codegen/meos-ops-free-manifest.json`):
+
+| Tier | Wiring class(es) here | Status in this package |
+|---|---|---|
+| `stateless` | [`MeosStatelessMap`](MeosStatelessMap.java) (generic `MapFunction`) · [`MeosStatelessFilter`](MeosStatelessFilter.java) (generic `FilterFunction`) | ✅ shipped |
+| `bounded-state` | [`MeosBoundedStateMap`](MeosBoundedStateMap.java) (generic `KeyedProcessFunction` with `ValueState` per key — state crosses the operator boundary as MEOS-WKB/WKT bytes so checkpoints/rescaling/savepoints are safe; raw `Pointer` never leaves the JVM-local operator instance) | ✅ shipped |
+| `windowed` | [`MeosWindowedAggregate`](MeosWindowedAggregate.java) (generic `ProcessWindowFunction`; window-close-only aggregation; no MEOS handles persist across window boundaries) | ✅ shipped |
+| `cross-stream` | [`MeosCrossStreamJoin`](MeosCrossStreamJoin.java) (generic `ProcessJoinFunction` over `KeyedStream.intervalJoin(other)`; time-bounded match window; same-key pairing) | ✅ shipped |
+| `io-meta` | covered transitively by the stateless wirings (no state, no window) | n/a |
+| `sequence-only` | inherently non-streamable — no wiring | n/a |
+
+The wirings are **generic**: each takes a serializable lambda
+forwarding to whichever generated `MeosOps*.f(...)` method the adopter
+needs. No per-method boilerplate, no per-method registration —
+adopters wire the entire ~800-method `stateless` slice through
+`MeosStatelessMap` / `MeosStatelessFilter` without touching this
+package.
+
+## Why DataStream rather than Table API
+
+The repo's existing pipeline (`berlinmod/`, `aisdata/`) is
+DataStream-API only. Sticking to DataStream avoids adding the
+~50 MB `flink-table-planner` runtime dependency to the build matrix.
+A Table-API-shaped sibling
+(`MeosOpsTableCatalogRegistrar` / `MeosScalarUDF` / `MeosAggregateFunction`)
+is a clean follow-up if/when the repo adopts Table API for other
+reasons.
+
+## How a generated MEOS call becomes a Flink operator
+
+The pattern is the same across all four tiers:
+
+```java
+// 1. Pick the generated MeosOps method
+// (Javadoc tier marker tells you which wiring to use)
+boolean overlap = MeosOpsTBox.overlaps_tbox_tbox(boxA, boxB); // tier = stateless
+
+// 2. Wrap with the matching wiring
+MeosStatelessFilter filter = MeosStatelessFilter.fromIntPredicate(
+ pair -> MeosOpsTBox.overlaps_tbox_tbox(pair.a, pair.b));
+
+// 3. Apply to the DataStream
+DataStream overlapping = stream.filter(filter);
+```
+
+`MEOS_AVAILABLE` is probed once per JVM by `MeosOpsRuntime`'s static
+initializer (shared across all `MeosOps*` and `MeosOpsFree*`
+facades). When unavailable, every generated method throws
+`UnsupportedOperationException` with a clear message — the wiring
+layer doesn't have to handle that itself.
+
+## End-to-end runnable demo
+
+[`demo/MeosWiringsDemoJob.java`](demo/MeosWiringsDemoJob.java) walks
+through a 3-stage DataStream pipeline using two of the generated
+facades wired through `MeosStatelessMap` + `MeosStatelessFilter`:
+
+1. Parse a stream of TBox WKT strings via
+ `MeosOpsFreeCore.tbox_in` (io-meta, no state).
+2. Filter to those overlapping a fixed query box via
+ `MeosOpsTBox.overlaps_tbox_tbox` (stateless predicate).
+3. Serialize each survivor to hex-WKB via
+ `MeosOpsTBox.tbox_as_hexwkb` (io-meta, no state).
+
+Run with:
+
+```bash
+mvn -q exec:java \
+ -Dexec.mainClass=org.mobilitydb.flink.meos.wirings.demo.MeosWiringsDemoJob \
+ -Dmeos.enabled=true
+```
+
+Output (expected): two `overlapping-tbox-hex` lines (the two input
+boxes that overlap the query box), one disjoint box dropped, one
+`MeosWirings stateless tier demo` job completion line.
+
+## Coexistence with `berlinmod.MEOSBridge`
+
+`MEOSBridge.java` is the BerlinMOD-specific, hand-written bridge for
+the 9-query streaming-form parity matrix — high-level and
+query-shaped. The wirings here are low-level and catalog-shaped —
+applicable to any of the ~800 stateless or 800 bounded-state
+generated facade methods, not just the BerlinMOD-9 subset. Both
+share the same `MEOS_AVAILABLE` discipline (`MeosOpsRuntime`).
diff --git a/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/demo/MeosAllTiersCapstoneDemo.java b/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/demo/MeosAllTiersCapstoneDemo.java
new file mode 100644
index 0000000..a9ebaed
--- /dev/null
+++ b/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/demo/MeosAllTiersCapstoneDemo.java
@@ -0,0 +1,246 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
+package org.mobilitydb.flink.meos.wirings.demo;
+
+import jnr.ffi.Pointer;
+import org.apache.flink.api.common.eventtime.WatermarkStrategy;
+import org.apache.flink.api.java.tuple.Tuple2;
+import org.apache.flink.api.java.tuple.Tuple4;
+import org.apache.flink.api.java.tuple.Tuple5;
+import org.apache.flink.streaming.api.datastream.DataStream;
+import org.apache.flink.streaming.api.datastream.KeyedStream;
+import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
+import org.apache.flink.streaming.api.windowing.assigners.TumblingEventTimeWindows;
+import org.apache.flink.streaming.api.windowing.time.Time;
+import org.apache.flink.streaming.api.windowing.windows.TimeWindow;
+import org.mobilitydb.meos.MeosOpsFreeCore;
+import org.mobilitydb.meos.MeosOpsTBox;
+import org.mobilitydb.flink.meos.wirings.MeosBoundedStateMap;
+import org.mobilitydb.flink.meos.wirings.MeosCrossStreamJoin;
+import org.mobilitydb.flink.meos.wirings.MeosStatelessFilter;
+import org.mobilitydb.flink.meos.wirings.MeosWindowedAggregate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.charset.StandardCharsets;
+import java.time.Duration;
+import java.util.Arrays;
+
+/**
+ * Capstone end-to-end demo composing ALL FOUR tier wirings in a single
+ * Flink DataStream pipeline.
+ *
+ * Proves the wirings compose into a realistic pipeline shape, not
+ * just work in isolation. Each tier-wiring class drives one stage of
+ * the pipeline:
+ *
+ *
{@code
+ * Stream A (vehicles) Stream B (queries)
+ * │ │
+ * ① MeosStatelessFilter │
+ * (keep events in regions of interest)│
+ * │ │
+ * ② MeosBoundedStateMap │
+ * (per-vehicle running tbox union) │
+ * │ │
+ * ③ MeosWindowedAggregate │
+ * (30s tumbling per-vehicle aggregate)│
+ * │ │
+ * └─────────────┐ ┌──────┘
+ * ↓ ↓
+ * ④ MeosCrossStreamJoin
+ * (interval-join: vehicle aggregates vs region queries
+ * within ±1m time bound, match by region key)
+ * ↓
+ * output
+ * }
+ *
+ * The pipeline answers: "for each region, which vehicles had an
+ * aggregate trajectory (running union) overlapping the region's
+ * query bbox during the latest 30-second window?"
+ *
+ *
Tier per stage:
+ *
+ * - Stateless filter — drop events outside any region of
+ * interest (per-event predicate, no state).
+ * - Bounded-state map — per-vehicle running tbox union
+ * (MEOS handle persisted across events as byte[] state).
+ * - Windowed aggregate — per-vehicle 30s tumbling tbox
+ * (window-close-only aggregation, no handle persistence across
+ * windows).
+ * - Cross-stream join — interval-join vehicle aggregates
+ * against region queries (pre-keyed by region, ±1m bound).
+ *
+ *
+ * Run with:
+ *
+ *
{@code
+ * mvn -q exec:java \
+ * -Dexec.mainClass=org.mobilitydb.flink.meos.wirings.demo.MeosAllTiersCapstoneDemo \
+ * -Dmeos.enabled=true
+ * }
+ */
+public final class MeosAllTiersCapstoneDemo {
+
+ private static final Logger LOG = LoggerFactory.getLogger(MeosAllTiersCapstoneDemo.class);
+
+ /** Region IDs we care about — the stateless filter drops events outside this set. */
+ private static final java.util.Set REGIONS_OF_INTEREST =
+ new java.util.HashSet<>(Arrays.asList(1, 2));
+
+ /** Vehicle event stream — (vehicleId, regionId, eventTboxWKT, eventTimeMs). */
+ private static final Tuple4[] VEHICLE_EVENTS = new Tuple4[]{
+ // window 1: [0s, 30s)
+ Tuple4.of(10, 1, "TBOX XT([0,2],[2026-01-01,2026-01-01 00:00:15])", ts("00:00:00")),
+ Tuple4.of(10, 1, "TBOX XT([1,3],[2026-01-01 00:00:15,2026-01-01 00:00:25])", ts("00:00:15")),
+ Tuple4.of(20, 2, "TBOX XT([10,12],[2026-01-01,2026-01-01 00:00:15])", ts("00:00:05")),
+ Tuple4.of(99, 9, "TBOX XT([90,92],[2026-01-01,2026-01-01 00:00:15])", ts("00:00:08")), // region 9 — dropped by stage 1
+ Tuple4.of(20, 2, "TBOX XT([11,13],[2026-01-01 00:00:15,2026-01-01 00:00:25])", ts("00:00:20")),
+ // window 2: [30s, 60s)
+ Tuple4.of(10, 1, "TBOX XT([0,4],[2026-01-01 00:00:30,2026-01-01 00:00:45])", ts("00:00:30")),
+ Tuple4.of(20, 2, "TBOX XT([10,15],[2026-01-01 00:00:30,2026-01-01 00:00:45])", ts("00:00:35")),
+ };
+
+ /** Region query stream — (regionId, queryTboxWKT, eventTimeMs). */
+ private static final Tuple2[] REGION_QUERIES = new Tuple2[]{
+ Tuple2.of(1, "TBOX XT([1,3],[2026-01-01 00:00:10,2026-01-01 00:00:25])"),
+ Tuple2.of(2, "TBOX XT([11,13],[2026-01-01 00:00:10,2026-01-01 00:00:25])"),
+ Tuple2.of(1, "TBOX XT([2,4],[2026-01-01 00:00:35,2026-01-01 00:00:50])"),
+ Tuple2.of(2, "TBOX XT([12,14],[2026-01-01 00:00:35,2026-01-01 00:00:50])"),
+ };
+
+ private static long ts(String hms) {
+ String[] parts = hms.split(":");
+ long secs = Integer.parseInt(parts[0]) * 3600L
+ + Integer.parseInt(parts[1]) * 60L
+ + Integer.parseInt(parts[2]);
+ return 1767225600000L + secs * 1000L;
+ }
+
+ public static void main(String[] args) throws Exception {
+ if (!MeosOpsTBox.MEOS_AVAILABLE) {
+ LOG.error("MEOS not available — the demo requires libmeos.");
+ System.exit(1);
+ }
+
+ StreamExecutionEnvironment env =
+ StreamExecutionEnvironment.getExecutionEnvironment();
+ env.setParallelism(1);
+
+ // ── Stream A: vehicle events ────────────────────────────────────────
+ DataStream> rawEvents =
+ env.fromCollection(Arrays.asList(VEHICLE_EVENTS))
+ .assignTimestampsAndWatermarks(
+ WatermarkStrategy
+ .>forBoundedOutOfOrderness(Duration.ofSeconds(1))
+ .withTimestampAssigner((e, t) -> e.f3));
+
+ // ── ① STATELESS FILTER ── keep only events in regions of interest ──
+ DataStream> inRegion =
+ rawEvents.filter(new MeosStatelessFilter>(
+ evt -> REGIONS_OF_INTEREST.contains(evt.f1)));
+
+ // ── ② BOUNDED-STATE MAP ── per-vehicle running tbox union ──────────
+ // State holds the MEOS-WKT text of the per-vehicle running union;
+ // emit (vehicleId, regionId, runningUnionWKT, eventTimeMs) per event.
+ DataStream> runningUnion = inRegion
+ .keyBy(t -> t.f0) // key by vehicleId
+ .process(new MeosBoundedStateMap, Tuple4>(
+ ptr -> MeosOpsTBox.tbox_out(ptr, 6).getBytes(StandardCharsets.UTF_8),
+ bytes -> MeosOpsTBox.tbox_in(new String(bytes, StandardCharsets.UTF_8)),
+ (prior, evt) -> {
+ Pointer eventTbox = MeosOpsTBox.tbox_in(evt.f2);
+ Pointer newUnion = (prior == null)
+ ? eventTbox
+ : MeosOpsFreeCore.union_tbox_tbox(prior, eventTbox, /*strict=*/false);
+ Tuple4 output =
+ Tuple4.of(evt.f0, evt.f1, MeosOpsTBox.tbox_out(newUnion, 6), evt.f3);
+ return new MeosBoundedStateMap.MeosStep<>(newUnion, output);
+ }))
+ .returns(org.apache.flink.api.common.typeinfo.TypeInformation.of(
+ new org.apache.flink.api.common.typeinfo.TypeHint>() {}));
+
+ // ── ③ WINDOWED AGGREGATE ── per-vehicle 30s tumbling tbox union ─────
+ // Within each 30s window: take the FINAL running-union value per
+ // vehicle as the per-window summary.
+ DataStream> windowed = runningUnion
+ .keyBy(t -> t.f0) // key by vehicleId
+ .window(TumblingEventTimeWindows.of(Time.seconds(30)))
+ .process(new MeosWindowedAggregate<
+ Integer,
+ Tuple4,
+ Tuple4,
+ TimeWindow
+ >((window, events, ctx) -> {
+ // Emit the LAST event in the window (the running union at window close).
+ Tuple4 last = null;
+ for (Tuple4 e : events) {
+ last = e;
+ }
+ return last;
+ }))
+ .returns(org.apache.flink.api.common.typeinfo.TypeInformation.of(
+ new org.apache.flink.api.common.typeinfo.TypeHint>() {}));
+
+ // ── Stream B: region queries (keyed by regionId for the join) ───────
+ DataStream> queryStream =
+ env.fromCollection(Arrays.asList(REGION_QUERIES))
+ .assignTimestampsAndWatermarks(
+ WatermarkStrategy
+ .>forBoundedOutOfOrderness(Duration.ofSeconds(1))
+ .withTimestampAssigner((e, t) -> ts("00:00:20"))); // single query-time
+
+ // ── ④ CROSS-STREAM JOIN ── vehicle aggregates × region queries ──────
+ // Pre-key both by regionId; interval-join within ±1m time bound.
+ // Per matched pair, emit (regionId, vehicleId, aggUnionWKT, queryWKT, vehicleTs).
+ KeyedStream, Integer> vehiclesKeyed =
+ windowed.keyBy(t -> t.f1); // key by regionId
+ KeyedStream, Integer> queriesKeyed =
+ queryStream.keyBy(q -> q.f0);
+
+ DataStream> overlaps =
+ vehiclesKeyed.intervalJoin(queriesKeyed)
+ .between(Time.minutes(-1), Time.minutes(1))
+ .process(new MeosCrossStreamJoin<
+ Tuple4,
+ Tuple2,
+ Tuple5
+ >((vehAgg, query, ctx) -> {
+ Pointer aggTbox = MeosOpsTBox.tbox_in(vehAgg.f2);
+ Pointer queryTbox = MeosOpsTBox.tbox_in(query.f1);
+ if (MeosOpsFreeCore.overlaps_tbox_tbox(aggTbox, queryTbox)) {
+ return Tuple5.of(vehAgg.f1, vehAgg.f0, vehAgg.f2, query.f1, vehAgg.f3);
+ }
+ return null;
+ }))
+ .returns(org.apache.flink.api.common.typeinfo.TypeInformation.of(
+ new org.apache.flink.api.common.typeinfo.TypeHint>() {}));
+
+ overlaps.print("capstone-output");
+
+ env.execute("MeosWirings capstone (all 4 tiers composed)");
+ }
+}
diff --git a/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/demo/MeosBoundedStateDemoJob.java b/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/demo/MeosBoundedStateDemoJob.java
new file mode 100644
index 0000000..bec7c25
--- /dev/null
+++ b/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/demo/MeosBoundedStateDemoJob.java
@@ -0,0 +1,139 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
+package org.mobilitydb.flink.meos.wirings.demo;
+
+import jnr.ffi.Pointer;
+import org.apache.flink.api.java.tuple.Tuple2;
+import org.apache.flink.streaming.api.datastream.DataStream;
+import org.apache.flink.streaming.api.datastream.KeyedStream;
+import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
+import org.mobilitydb.meos.MeosOpsFreeCore;
+import org.mobilitydb.meos.MeosOpsTBox;
+import org.mobilitydb.flink.meos.wirings.MeosBoundedStateMap;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+/**
+ * End-to-end runnable demo of the {@code bounded-state} tier wiring.
+ *
+ * Pipeline:
+ *
+ * - Stream of {@code (vehicleId, eventTboxWKT)} for 2 vehicles, 3
+ * events each.
+ * - {@code keyBy(vehicleId)} so per-vehicle state isolates.
+ * - Per-vehicle running tbox union via
+ * {@link MeosBoundedStateMap}: state holds the MEOS-WKT text of
+ * the current union; on each event, deserialize → call
+ * {@code MeosOpsFreeCore.union_tbox_tbox} → re-serialize.
+ * - Emit {@code (vehicleId, runningUnionTboxWKT)} per event.
+ *
+ *
+ * What the demo proves:
+ *
+ * - Checkpoint-safe state — state crosses the operator
+ * boundary as {@code byte[]} (MEOS-WKT here, MEOS-WKB in
+ * production); no raw native pointers in checkpoints.
+ * - Per-key isolation — vehicle 1's running union does not
+ * leak into vehicle 2's, and vice versa.
+ * - First-event correctness — the wiring handles
+ * {@code prior == null} on the first event for each key by
+ * skipping deserialize and seeding state with the first event's
+ * tbox.
+ *
+ *
+ * Run with:
+ *
+ *
{@code
+ * mvn -q exec:java \
+ * -Dexec.mainClass=org.mobilitydb.flink.meos.wirings.demo.MeosBoundedStateDemoJob \
+ * -Dmeos.enabled=true
+ * }
+ *
+ * Expected output: 6 lines (3 per vehicle), each showing the growing
+ * union tbox after that event.
+ */
+public final class MeosBoundedStateDemoJob {
+
+ private static final Logger LOG = LoggerFactory.getLogger(MeosBoundedStateDemoJob.class);
+
+ /** 6 events across 2 vehicles — the running union grows monotonically per key. */
+ private static final Tuple2[] EVENTS = new Tuple2[]{
+ Tuple2.of(1, "TBOX XT([0,2],[2026-01-01,2026-01-01 01:00])"),
+ Tuple2.of(2, "TBOX XT([10,12],[2026-01-01,2026-01-01 01:00])"),
+ Tuple2.of(1, "TBOX XT([3,5],[2026-01-01 01:00,2026-01-01 02:00])"),
+ Tuple2.of(2, "TBOX XT([13,15],[2026-01-01 01:00,2026-01-01 02:00])"),
+ Tuple2.of(1, "TBOX XT([1,4],[2026-01-01 02:00,2026-01-01 03:00])"),
+ Tuple2.of(2, "TBOX XT([11,14],[2026-01-01 02:00,2026-01-01 03:00])"),
+ };
+
+ public static void main(String[] args) throws Exception {
+ if (!MeosOpsTBox.MEOS_AVAILABLE) {
+ LOG.error("MEOS not available — the demo requires libmeos.");
+ System.exit(1);
+ }
+
+ StreamExecutionEnvironment env =
+ StreamExecutionEnvironment.getExecutionEnvironment();
+ env.setParallelism(1);
+
+ DataStream> events =
+ env.fromCollection(Arrays.asList(EVENTS));
+
+ KeyedStream, Integer> keyed =
+ events.keyBy(t -> t.f0);
+
+ // Wire the per-vehicle running union via MeosBoundedStateMap.
+ // State is the MEOS-WKT text of the current union (byte[] form);
+ // each event deserializes, unions with the event tbox, re-serializes.
+ DataStream> runningUnion = keyed.process(
+ new MeosBoundedStateMap, Tuple2>(
+ /* serialize: Pointer → byte[] */
+ ptr -> MeosOpsTBox.tbox_out(ptr, 6).getBytes(StandardCharsets.UTF_8),
+ /* deserialize: byte[] → Pointer */
+ bytes -> MeosOpsTBox.tbox_in(new String(bytes, StandardCharsets.UTF_8)),
+ /* step: (prior union, this event) → (new union, output) */
+ (prior, evt) -> {
+ Pointer eventTbox = MeosOpsTBox.tbox_in(evt.f1);
+ // First event for a key: prior is null — seed with the event's tbox.
+ // Subsequent events: union prior with the new event's tbox.
+ Pointer newUnion = (prior == null)
+ ? eventTbox
+ : MeosOpsFreeCore.union_tbox_tbox(prior, eventTbox, /*strict=*/false);
+ Tuple2 output =
+ Tuple2.of(evt.f0, MeosOpsTBox.tbox_out(newUnion, 6));
+ return new MeosBoundedStateMap.MeosStep<>(newUnion, output);
+ }))
+ .returns(org.apache.flink.api.common.typeinfo.TypeInformation.of(
+ new org.apache.flink.api.common.typeinfo.TypeHint>() {}));
+
+ runningUnion.print("running-union");
+
+ env.execute("MeosWirings bounded-state tier demo");
+ }
+}
diff --git a/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/demo/MeosCrossStreamDemoJob.java b/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/demo/MeosCrossStreamDemoJob.java
new file mode 100644
index 0000000..4b727dc
--- /dev/null
+++ b/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/demo/MeosCrossStreamDemoJob.java
@@ -0,0 +1,160 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
+package org.mobilitydb.flink.meos.wirings.demo;
+
+import jnr.ffi.Pointer;
+import org.apache.flink.api.common.eventtime.WatermarkStrategy;
+import org.apache.flink.api.java.tuple.Tuple4;
+import org.apache.flink.api.java.tuple.Tuple5;
+import org.apache.flink.streaming.api.datastream.DataStream;
+import org.apache.flink.streaming.api.datastream.KeyedStream;
+import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
+import org.apache.flink.streaming.api.windowing.time.Time;
+import org.mobilitydb.meos.MeosOpsFreeCore;
+import org.mobilitydb.meos.MeosOpsTBox;
+import org.mobilitydb.flink.meos.wirings.MeosCrossStreamJoin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.time.Duration;
+import java.util.Arrays;
+
+/**
+ * End-to-end runnable demo of the {@code cross-stream} tier wiring.
+ *
+ * Pipeline:
+ *
+ * - Two parallel streams, each carrying {@code (regionId,
+ * vehicleId, tboxWKT, eventTimeMs)}, sharing the {@code regionId}
+ * key so cross-stream pairing is per-region.
+ * - {@code keyBy(regionId)} on both, then
+ * {@code .intervalJoin().between(-1m, +1m)} so each event in
+ * stream A is matched with events in stream B within ±1 minute
+ * in the same region.
+ * - {@link MeosCrossStreamJoin}: for each matched pair, test
+ * whether the two tboxes overlap via
+ * {@code MeosOpsFreeCore.overlaps_tbox_tbox}; if yes, emit
+ * {@code (regionId, vehAId, vehBId, leftTs, rightTs)}.
+ *
+ *
+ * What the demo proves:
+ *
+ * - Interval-join semantics — only pairs within the time
+ * bound are matched; outside-window events are skipped.
+ * - Per-key isolation — events in region 1 don't match
+ * events in region 2, even if their timestamps overlap.
+ * - Pairwise MEOS call — the wiring lambda receives both
+ * matched events; the adopter calls any cross-stream MeosOps
+ * method on the pair (here {@code overlaps_tbox_tbox}, which
+ * is technically stateless on box pairs but the join-pairing
+ * is what makes it cross-stream).
+ *
+ *
+ * Run with:
+ *
+ *
{@code
+ * mvn -q exec:java \
+ * -Dexec.mainClass=org.mobilitydb.flink.meos.wirings.demo.MeosCrossStreamDemoJob \
+ * -Dmeos.enabled=true
+ * }
+ */
+public final class MeosCrossStreamDemoJob {
+
+ private static final Logger LOG = LoggerFactory.getLogger(MeosCrossStreamDemoJob.class);
+
+ /** Stream A — vehicle events, 3 per region across 2 regions. */
+ private static final Tuple4[] EVENTS_A = new Tuple4[]{
+ Tuple4.of(1, 10, "TBOX XT([0,5],[2026-01-01,2026-01-01 00:00:30])", ts("00:00:00")),
+ Tuple4.of(2, 20, "TBOX XT([100,105],[2026-01-01,2026-01-01 00:00:30])", ts("00:00:05")),
+ Tuple4.of(1, 11, "TBOX XT([10,15],[2026-01-01 00:00:30,2026-01-01 00:01:00])", ts("00:00:30")),
+ };
+
+ /** Stream B — different vehicles, 3 per region across 2 regions. */
+ private static final Tuple4[] EVENTS_B = new Tuple4[]{
+ Tuple4.of(1, 30, "TBOX XT([3,8],[2026-01-01,2026-01-01 00:00:30])", ts("00:00:10")), // overlaps with A:(1,10)
+ Tuple4.of(2, 40, "TBOX XT([200,205],[2026-01-01,2026-01-01 00:00:30])", ts("00:00:15")), // disjoint from A:(2,20)
+ Tuple4.of(1, 31, "TBOX XT([12,17],[2026-01-01 00:00:30,2026-01-01 00:01:00])", ts("00:00:40")), // overlaps with A:(1,11)
+ };
+
+ private static long ts(String hms) {
+ String[] parts = hms.split(":");
+ long secs = Integer.parseInt(parts[0]) * 3600L
+ + Integer.parseInt(parts[1]) * 60L
+ + Integer.parseInt(parts[2]);
+ return 1767225600000L + secs * 1000L; // 2026-01-01T00:00:00 UTC in ms
+ }
+
+ public static void main(String[] args) throws Exception {
+ if (!MeosOpsTBox.MEOS_AVAILABLE) {
+ LOG.error("MEOS not available — the demo requires libmeos.");
+ System.exit(1);
+ }
+
+ StreamExecutionEnvironment env =
+ StreamExecutionEnvironment.getExecutionEnvironment();
+ env.setParallelism(1);
+
+ DataStream> a =
+ env.fromCollection(Arrays.asList(EVENTS_A))
+ .assignTimestampsAndWatermarks(
+ WatermarkStrategy
+ .>forBoundedOutOfOrderness(Duration.ofSeconds(1))
+ .withTimestampAssigner((e, ts) -> e.f3));
+ DataStream> b =
+ env.fromCollection(Arrays.asList(EVENTS_B))
+ .assignTimestampsAndWatermarks(
+ WatermarkStrategy
+ .>forBoundedOutOfOrderness(Duration.ofSeconds(1))
+ .withTimestampAssigner((e, ts) -> e.f3));
+
+ KeyedStream, Integer> aKeyed = a.keyBy(t -> t.f0);
+ KeyedStream, Integer> bKeyed = b.keyBy(t -> t.f0);
+
+ // Interval-join: pair events in A with events in B within ±1 minute, same region key.
+ DataStream> overlaps =
+ aKeyed.intervalJoin(bKeyed)
+ .between(Time.minutes(-1), Time.minutes(1))
+ .process(new MeosCrossStreamJoin<
+ Tuple4, // L
+ Tuple4, // R
+ Tuple5 // OUT: (region, vehA, vehB, lts, rts)
+ >((left, right, ctx) -> {
+ Pointer leftTbox = MeosOpsTBox.tbox_in(left.f2);
+ Pointer rightTbox = MeosOpsTBox.tbox_in(right.f2);
+ if (MeosOpsFreeCore.overlaps_tbox_tbox(leftTbox, rightTbox)) {
+ return Tuple5.of(left.f0, left.f1, right.f1,
+ ctx.getLeftTimestamp(), ctx.getRightTimestamp());
+ }
+ return null;
+ }))
+ .returns(org.apache.flink.api.common.typeinfo.TypeInformation.of(
+ new org.apache.flink.api.common.typeinfo.TypeHint>() {}));
+
+ overlaps.print("cross-stream-overlap");
+
+ env.execute("MeosWirings cross-stream tier demo");
+ }
+}
diff --git a/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/demo/MeosWindowedDemoJob.java b/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/demo/MeosWindowedDemoJob.java
new file mode 100644
index 0000000..52b79e2
--- /dev/null
+++ b/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/demo/MeosWindowedDemoJob.java
@@ -0,0 +1,157 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
+package org.mobilitydb.flink.meos.wirings.demo;
+
+import jnr.ffi.Pointer;
+import org.apache.flink.api.common.eventtime.WatermarkStrategy;
+import org.apache.flink.api.java.tuple.Tuple3;
+import org.apache.flink.api.java.tuple.Tuple4;
+import org.apache.flink.streaming.api.datastream.DataStream;
+import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
+import org.apache.flink.streaming.api.windowing.assigners.TumblingEventTimeWindows;
+import org.apache.flink.streaming.api.windowing.time.Time;
+import org.apache.flink.streaming.api.windowing.windows.TimeWindow;
+import org.mobilitydb.meos.MeosOpsFreeCore;
+import org.mobilitydb.meos.MeosOpsTBox;
+import org.mobilitydb.flink.meos.wirings.MeosWindowedAggregate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.time.Duration;
+import java.util.Arrays;
+
+/**
+ * End-to-end runnable demo of the {@code windowed} tier wiring.
+ *
+ * Pipeline:
+ *
+ * - Stream of {@code (vehicleId, tboxWKT, eventTimeMs)} for 2
+ * vehicles, 4 events each.
+ * - {@code assignTimestampsAndWatermarks} so event-time windows
+ * fire on a bounded-out-of-orderness schedule.
+ * - {@code keyBy(vehicleId)} → 30-second tumbling event-time
+ * window.
+ * - Per-window {@link MeosWindowedAggregate}: union all in-window
+ * event tboxes into a single per-window aggregate tbox via
+ * repeated {@code MeosOpsFreeCore.union_tbox_tbox}, emit
+ * {@code (vehicleId, windowStart, eventCount, aggregateTboxWKT)}.
+ *
+ *
+ * What the demo proves:
+ *
+ * - Window-close timing — events outside the window are
+ * excluded; events within are aggregated together.
+ * - Per-key isolation — vehicle 1's window aggregate does
+ * not include vehicle 2's events, and vice versa.
+ * - Stateless aggregation — unlike {@code bounded-state},
+ * no MEOS handle persists across window boundaries; each window
+ * builds its aggregate from scratch from the iterable.
+ *
+ *
+ * Run with:
+ *
+ *
{@code
+ * mvn -q exec:java \
+ * -Dexec.mainClass=org.mobilitydb.flink.meos.wirings.demo.MeosWindowedDemoJob \
+ * -Dmeos.enabled=true
+ * }
+ *
+ * Expected output: 4 lines (2 windows × 2 vehicles), each showing
+ * the aggregate tbox spanning that window's events for that vehicle.
+ */
+public final class MeosWindowedDemoJob {
+
+ private static final Logger LOG = LoggerFactory.getLogger(MeosWindowedDemoJob.class);
+
+ /** 8 events across 2 vehicles, two 30s windows each. */
+ private static final Tuple3[] EVENTS = new Tuple3[]{
+ // window 1: [0s, 30s)
+ Tuple3.of(1, "TBOX XT([0,2],[2026-01-01,2026-01-01 00:00:10])", ts("00:00:00")),
+ Tuple3.of(2, "TBOX XT([10,12],[2026-01-01,2026-01-01 00:00:10])", ts("00:00:05")),
+ Tuple3.of(1, "TBOX XT([3,5],[2026-01-01 00:00:10,2026-01-01 00:00:20])", ts("00:00:10")),
+ Tuple3.of(2, "TBOX XT([13,15],[2026-01-01 00:00:10,2026-01-01 00:00:20])", ts("00:00:15")),
+ // window 2: [30s, 60s)
+ Tuple3.of(1, "TBOX XT([1,4],[2026-01-01 00:00:30,2026-01-01 00:00:40])", ts("00:00:30")),
+ Tuple3.of(2, "TBOX XT([11,14],[2026-01-01 00:00:30,2026-01-01 00:00:40])", ts("00:00:35")),
+ Tuple3.of(1, "TBOX XT([2,3],[2026-01-01 00:00:40,2026-01-01 00:00:50])", ts("00:00:40")),
+ Tuple3.of(2, "TBOX XT([12,13],[2026-01-01 00:00:40,2026-01-01 00:00:50])", ts("00:00:45")),
+ };
+
+ /** Convert "HH:MM:SS" relative to 2026-01-01T00:00:00 into epoch milliseconds. */
+ private static long ts(String hms) {
+ String[] parts = hms.split(":");
+ long secs = Integer.parseInt(parts[0]) * 3600L
+ + Integer.parseInt(parts[1]) * 60L
+ + Integer.parseInt(parts[2]);
+ return 1767225600000L + secs * 1000L; // 2026-01-01T00:00:00 UTC in ms
+ }
+
+ public static void main(String[] args) throws Exception {
+ if (!MeosOpsTBox.MEOS_AVAILABLE) {
+ LOG.error("MEOS not available — the demo requires libmeos.");
+ System.exit(1);
+ }
+
+ StreamExecutionEnvironment env =
+ StreamExecutionEnvironment.getExecutionEnvironment();
+ env.setParallelism(1);
+
+ DataStream> events =
+ env.fromCollection(Arrays.asList(EVENTS))
+ .assignTimestampsAndWatermarks(
+ WatermarkStrategy
+ .>forBoundedOutOfOrderness(Duration.ofSeconds(1))
+ .withTimestampAssigner((e, ts) -> e.f2));
+
+ DataStream> aggregates = events
+ .keyBy(t -> t.f0)
+ .window(TumblingEventTimeWindows.of(Time.seconds(30)))
+ .process(new MeosWindowedAggregate<
+ Integer, // K
+ Tuple3, // IN
+ Tuple4, // OUT
+ TimeWindow // W
+ >((window, inWindowEvents, ctx) -> {
+ Pointer agg = null;
+ int count = 0;
+ for (Tuple3 evt : inWindowEvents) {
+ Pointer evtTbox = MeosOpsTBox.tbox_in(evt.f1);
+ agg = (agg == null)
+ ? evtTbox
+ : MeosOpsFreeCore.union_tbox_tbox(agg, evtTbox, /*strict=*/false);
+ count++;
+ }
+ String aggWkt = (agg == null) ? "(empty)" : MeosOpsTBox.tbox_out(agg, 6);
+ return Tuple4.of(ctx.getCurrentKey(), window.getStart(), count, aggWkt);
+ }))
+ .returns(org.apache.flink.api.common.typeinfo.TypeInformation.of(
+ new org.apache.flink.api.common.typeinfo.TypeHint>() {}));
+
+ aggregates.print("window-aggregate");
+
+ env.execute("MeosWirings windowed tier demo");
+ }
+}
diff --git a/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/demo/MeosWiringsDemoJob.java b/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/demo/MeosWiringsDemoJob.java
new file mode 100644
index 0000000..99b06c5
--- /dev/null
+++ b/flink-processor/src/main/java/org/mobilitydb/flink/meos/wirings/demo/MeosWiringsDemoJob.java
@@ -0,0 +1,126 @@
+/*****************************************************************************
+ *
+ * This MobilityDB code is provided under The PostgreSQL License.
+ * Copyright (c) 2020-2026, Université libre de Bruxelles and MobilityDB
+ * contributors
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without a written
+ * agreement is hereby granted, provided that the above copyright notice and
+ * this paragraph and the following two paragraphs appear in all copies.
+ *
+ * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+ * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+ * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
+ * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ *****************************************************************************/
+
+package org.mobilitydb.flink.meos.wirings.demo;
+
+import org.apache.flink.api.common.typeinfo.Types;
+import org.apache.flink.streaming.api.datastream.DataStream;
+import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
+import org.mobilitydb.meos.MeosOpsFreeCore;
+import org.mobilitydb.meos.MeosOpsTBox;
+import org.mobilitydb.flink.meos.wirings.MeosStatelessFilter;
+import org.mobilitydb.flink.meos.wirings.MeosStatelessMap;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Arrays;
+
+/**
+ * End-to-end runnable demo showing how the generated
+ * {@code org.mobilitydb.meos.MeosOps*} facades wire into a Flink
+ * {@code DataStream} pipeline through the
+ * {@code org.mobilitydb.flink.meos.wirings} helpers.
+ *
+ * The demo:
+ *
+ * - Builds a small in-memory stream of TBox-WKT strings.
+ * - Parses each into a JMEOS {@code Pointer} via
+ * {@code MeosOpsTBox.tbox_in} (tier = {@code io-meta}).
+ * - Filters to those that overlap with a fixed query TBox via
+ * {@code MeosOpsTBox.overlaps_tbox_tbox} wrapped as a
+ * {@link MeosStatelessFilter} (tier = {@code stateless}).
+ * - Maps each surviving TBox to its serialized WKB hex via
+ * {@code MeosOpsTBox.tbox_as_hexwkb} wrapped as a
+ * {@link MeosStatelessMap} (tier = {@code io-meta} but no per-key
+ * state, so the {@code stateless} wiring works for it too).
+ *
+ *
+ * Run with:
+ *
+ *
{@code
+ * mvn -q exec:java \
+ * -Dexec.mainClass=org.mobilitydb.flink.meos.wirings.demo.MeosWiringsDemoJob \
+ * -Dmeos.enabled=true # require libmeos loadable
+ * }
+ *
+ * If libmeos is not loadable on the runtime (or
+ * {@code -Dmeos.enabled=false}), every wrapped MeosOps
+ * call throws {@code UnsupportedOperationException} with a clear
+ * message — the demo prints the throw shape and exits non-zero.
+ */
+public final class MeosWiringsDemoJob {
+
+ private static final Logger LOG = LoggerFactory.getLogger(MeosWiringsDemoJob.class);
+
+ /** A small box covering (xmin=0, ymin=0, xmax=10, ymax=10). */
+ private static final String QUERY_TBOX_WKT = "TBOX XT([0,10],[2026-01-01,2026-01-02])";
+
+ /** Three input boxes — two overlap the query box, one doesn't. */
+ private static final String[] INPUT_TBOX_WKTS = {
+ "TBOX XT([5,15],[2026-01-01,2026-01-02])", // overlaps
+ "TBOX XT([20,30],[2026-01-01,2026-01-02])", // disjoint
+ "TBOX XT([3,8],[2026-01-01,2026-01-02])", // overlaps
+ };
+
+ public static void main(String[] args) throws Exception {
+ // Probe MEOS availability (the static initializer in MeosOpsRuntime
+ // fires the first time any MeosOps class is touched).
+ if (!MeosOpsTBox.MEOS_AVAILABLE) {
+ LOG.error("MEOS not available — the demo requires libmeos. "
+ + "Set -Dmeos.enabled=true and ensure libmeos is loadable.");
+ System.exit(1);
+ }
+
+ StreamExecutionEnvironment env =
+ StreamExecutionEnvironment.getExecutionEnvironment();
+ env.setParallelism(1);
+
+ DataStream tboxWkts = env.fromCollection(Arrays.asList(INPUT_TBOX_WKTS));
+
+ // The records crossing operator boundaries are serialized MEOS values
+ // (WKT text) — never raw native pointers, which are process-local and
+ // not serializable across Flink tasks. Each operator parses to a
+ // transient MEOS handle, calls MEOS, and re-serializes.
+
+ // Stage 1: parse each WKT and re-serialize via tbox_out (stateless io-meta).
+ DataStream normalized = tboxWkts.map(
+ new MeosStatelessMap