diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
new file mode 100644
index 0000000..fb020e7
--- /dev/null
+++ b/.github/workflows/docker.yml
@@ -0,0 +1,24 @@
+name: Docker build
+
+on:
+ pull_request:
+ types: [opened, synchronize, reopened]
+
+jobs:
+ jpo-deduplicator:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v2
+ - name: Build
+ uses: docker/build-push-action@v3
+ with:
+ context: jpo-deduplicator
+ build-args: |
+ MAVEN_GITHUB_TOKEN_NAME=${{ vars.MAVEN_GITHUB_TOKEN_NAME }}
+ MAVEN_GITHUB_TOKEN=${{ secrets.MAVEN_GITHUB_TOKEN }}
+ MAVEN_GITHUB_ORG=${{ github.repository_owner }}
+ secrets: |
+ MAVEN_GITHUB_TOKEN: ${{ secrets.MAVEN_GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/.github/workflows/dockerhub.yml b/.github/workflows/dockerhub.yml
new file mode 100644
index 0000000..00e2704
--- /dev/null
+++ b/.github/workflows/dockerhub.yml
@@ -0,0 +1,39 @@
+name: "DockerHub Build and Push"
+
+on:
+ push:
+ branches:
+ - "develop"
+ - "master"
+ - "release/*"
+
+jobs:
+ dockerhub-jpo-deduplicator:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v2
+ - name: Login to DockerHub
+ uses: docker/login-action@v2
+ with:
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
+ password: ${{ secrets.DOCKERHUB_TOKEN }}
+
+ - name: Replcae Docker tag
+ id: set_tag
+ run: echo "TAG=$(echo ${GITHUB_REF##*/} | sed 's/\//-/g')" >> $GITHUB_ENV
+
+ - name: Build
+ uses: docker/build-push-action@v3
+ with:
+ context: jpo-deduplicator
+ push: true
+ tags: usdotjpoode/jpo-deduplicator:${{ env.TAG }}
+ build-args: |
+ MAVEN_GITHUB_TOKEN_NAME=${{ vars.MAVEN_GITHUB_TOKEN_NAME }}
+ MAVEN_GITHUB_TOKEN=${{ secrets.MAVEN_GITHUB_TOKEN }}
+ MAVEN_GITHUB_ORG=${{ github.repository_owner }}
+ secrets: |
+ MAVEN_GITHUB_TOKEN: ${{ secrets.MAVEN_GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 845959d..8d161e5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,3 @@
-**/.env
\ No newline at end of file
+**/.env
+
+**/target
\ No newline at end of file
diff --git a/README.md b/README.md
index c1c0580..ea2e9d4 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,12 @@ The JPO ITS utilities repository serves as a central location for deploying open
- [Quick Run](#quick-run-1)
- [4. MongoDB Kafka Connect](#4-mongodb-kafka-connect)
- [Configuration](#configuration)
+ - [Configure Kafka Connector Creation](#configure-kafka-connector-creation)
- [Quick Run](#quick-run-2)
+ - [5. Deduplicator](#5-jpo-Deduplicator)
+ - [Deduplication Configuration](#deduplication-config)
+ - [Github Token Generation](#generate-a-github-token)
+ - [Quick Run](#quick-run-3)
@@ -88,7 +93,7 @@ An optional `kafka-init`, `schema-registry`, and `kafka-ui` instance can be depl
### Configure Topic Creation
-The Kafka topics created by the `kafka-setup` service are configured in the [kafka-topics-values.yaml](kafka/kafka-topics-values.yaml) file. The topics in that file are organized by the application, and sorted into "Stream Topics" (those with `cleanup.policy` = `delete`) and "Table Topics" (with `cleanup.policy` = `compact`).
+The Kafka topics created by the `kafka-setup` service are configured in the [kafka-topics-values.yaml](jikkou/kafka-topics-values.yaml) file. The topics in that file are organized by the application, and sorted into "Stream Topics" (those with `cleanup.policy` = `delete`) and "Table Topics" (with `cleanup.policy` = `compact`).
The following enviroment variables can be used to configure Kafka Topic creation.
@@ -103,8 +108,7 @@ The following enviroment variables can be used to configure Kafka Topic creation
| `KAFKA_TOPIC_MIN_INSYNC_REPLICAS` | Minumum number of in-sync replicas (for use with ack=all) |
| `KAFKA_TOPIC_RETENTION_MS` | Retention time for stream topics, milliseconds |
| `KAFKA_TOPIC_DELETE_RETENTION_MS` | Tombstone retention time for compacted topics, milliseconds |
-
-
+| `KAFKA_TOPIC_CONFIG_RELATIVE_PATH` | Relative path to the Kafka topic yaml configuration script, upper level directories are supported |
### Quick Run
@@ -121,34 +125,49 @@ The following enviroment variables can be used to configure Kafka Topic creation
## 4. MongoDB Kafka Connect
-The mongo-connector service connects to specified Kafka topics (as defined in the mongo-connector/connect_start.sh script) and deposits these messages to separate collections in the MongoDB Database. The codebase that provides this functionality comes from Confluent using their community licensed [cp-kafka-connect image](https://hub.docker.com/r/confluentinc/cp-kafka-connect). Documentation for this image can be found [here](https://docs.confluent.io/platform/current/connect/index.html#what-is-kafka-connect).
+The mongo-connector service connects to specified Kafka topics and deposits these messages to separate collections in the MongoDB Database. The codebase that provides this functionality comes from Confluent using their community licensed [cp-kafka-connect image](https://hub.docker.com/r/confluentinc/cp-kafka-connect). Documentation for this image can be found [here](https://docs.confluent.io/platform/current/connect/index.html#what-is-kafka-connect).
### Configuration
-Provided in the mongo-connector directory is a sample configuration shell script ([connect_start.sh](./kafka-connect/connect_start.sh)) that can be used to create kafka connectors to MongoDB. The connectors in kafka connect are defined in the format that follows:
-
-``` shell
-declare -A config_name=([name]="topic_name" [collection]="mongo_collection_name"
- [convert_timestamp]=true [timefield]="timestamp" [use_key]=true [key]="key" [add_timestamp]=true)
-```
-
-The format above describes the basic configuration for configuring a sink connector, this should be placed at the beginning of the connect_start.sh file. In general we recommend to keep the MongoDB collection name the same as the topic name to avoid confusion. Additionally, if there is a top level timefield set `convert_timestamp` to true and then specify the time field name that appears in the message. This will allow MongoDB to transform that message into a date object to allow for TTL creation and reduce message size. To override MongoDB's default message `_id` field, set `use_key` to true and then set the `key` property to "key". The "add_timestamp" field defines whether the connector will add a auto generated timestamp to each document. This allows for creation of Time To Live (TTL) indexes on the collections to help limit collection size growth.
-
-After the sink connector is configured above, then make sure to call the createSink function with the config_name of the configuration like so:
-``` shell
-createSink config_name
-```
-
-This needs to be put after the createSink function definition. To use a different `connect_start.sh` script, pass in the relative path of the new script by overriding the `CONNECT_SCRIPT_RELATIVE_PATH` environmental variable.
+Kafka connectors are managed by the
Set the `COMPOSE_PROFILES` environmental variable as follows:
-- `kafka_connect` will only spin up the `kafka-connect` service in [docker-compose-connect](docker-compose-connect.yml)
+- `kafka_connect` will only spin up the `kafka-connect` and `kafka-init` services in [docker-compose-connect](docker-compose-connect.yml)
- NOTE: This implies that you will be using a separate Kafka and MongoDB cluster
- `kafka_connect_standalone` will run the following:
1. `kafka-connect` service from [docker-compose-connect](docker-compose-connect.yml)
- 2. `kafka` service from [docker-compose-kafka](docker-compose-kafka.yml)
- 3. `mongo` and `mongo-setup` services from [docker-compose-mongo](docker-compose-mongo.yml)
+ 2. `kafka-init` service from [docker-compose-connect](docker-compose-connect.yml)
+ 3. `kafka` service from [docker-compose-kafka](docker-compose-kafka.yml)
+ 4. `mongo` and `mongo-setup` services from [docker-compose-mongo](docker-compose-mongo.yml)
+
+### Configure Kafka Connector Creation
+
+The Kafka connectors created by the `kafka-connect-setup` service are configured in the [kafka-connectors-values.yaml](jikkou/kafka-connectors-values.yaml) file. The connectors in that file are organized by the application, and given parameters to define the Kafka -> MongoDB sync connector:
+
+| Connector Variable | Required | Condition | Description|
+|---|---|---|---|
+| `topicName` | Yes | Always | The name of the Kafka topic to sync from |
+| `collectionName` | Yes | Always | The name of the MongoDB collection to write to |
+| `generateTimestamp` | No | Optional | Enable or disable adding a timestamp to each message (true/false) |
+| `connectorName` | No | Optional | Override the name of the connector from the `collectionName` to this field instead |
+| `useTimestamp` | No | Optional | Converts the `timestampField` field at the top level of the value to a BSON date |
+| `timestampField` | No | Required if `useTimestamp` is `true` | The name of the timestamp field at the top level of the message |
+| `useKey` | No | Optional | Override the document `_id` field in MongoDB to use a specified `keyField` from the message |
+| `keyField` | No | Required if `useKey` is `true` | The name of the key field |
+
+The following environment variables can be used to configure Kafka Connectors:
+
+| Environment Variable | Description |
+|---|---|
+| `CONNECT_URL` | Kafka connect API URL |
+| `CONNECT_LOG_LEVEL` | Kafka connect log level (`OFF`, `ERROR`, `WARN`, `INFO`) |
+| `CONNECT_TASKS_MAX` | Number of concurrent tasks to configure on kafka connectors |
+| `CONNECT_CREATE_ODE` | Whether to create kafka connectors for the ODE |
+| `CONNECT_CREATE_GEOJSONCONVERTER` | Whether to create topics for the GeojsonConverter |
+| `CONNECT_CREATE_CONFLICTMONITOR` | Whether to create kafka connectors for the Conflict Monitor |
+| `CONNECT_CREATE_DEDUPLICATOR` | Whether to create topics for the Deduplicator |
+| `CONNECT_CONFIG_RELATIVE_PATH` | Relative path to the Kafka connector yaml configuration script, upper level directories are supported |
### Quick Run
@@ -170,4 +189,70 @@ Set the `COMPOSE_PROFILES` environmental variable as follows:
3. Click `OdeBsmJson`, and now you should see your message!
8. Feel free to test this with other topics or by producing to these topics using the [ODE](https://github.com/usdot-jpo-ode/jpo-ode)
+
+
+
+## 5. jpo-deduplicator
+The JPO-Deduplicator is a Kafka Java spring-boot application designed to reduce the number of messages stored and processed in the ODE system. This is done by reading in messages from an input topic (such as topic.ProcessedMap) and outputting a subset of those messages on a related output topic (topic.DeduplicatedProcessedMap). Functionally, this is done by removing deduplicate messages from the input topic and only passing on unique messages. In addition, each topic will pass on at least 1 message per hour even if the message is a duplicate. This behavior helps ensure messages are still flowing through the system. The following topics currently support deduplication.
+
+- topic.ProcessedMap -> topic.DeduplicatedProcessedMap
+- topic.ProcessedMapWKT -> topic.DeduplicatedProcessedMapWKT
+- topic.OdeMapJson -> topic.DeduplicatedOdeMapJson
+- topic.OdeTimJson -> topic.DeduplicatedOdeTimJson
+- topic.OdeRawEncodedTIMJson -> topic.DeduplicatedOdeRawEncodedTIMJson
+- topic.OdeBsmJson -> topic.DeduplicatedOdeBsmJson
+- topic.ProcessedSpat -> topic.DeduplicatedProcessedSpat
+
+### Deduplication Config
+
+When running the jpo-deduplication as a submodule in jpo-utils, the deduplicator will automatically turn on deduplication for a topic when that topic is created. For example if the KAFKA_TOPIC_CREATE_GEOJSONCONVERTER environment variable is set to true, the deduplicator will start performing deduplication for ProcessedMap, ProcessedMapWKT, and ProcessedSpat data.
+
+To manually configure deduplication for a topic, the following environment variables can also be used.
+
+| Environment Variable | Description |
+|---|---|
+| `ENABLE_PROCESSED_MAP_DEDUPLICATION` | `true` / `false` - Enable ProcessedMap message Deduplication |
+| `ENABLE_PROCESSED_MAP_WKT_DEDUPLICATION` | `true` / `false` - Enable ProcessedMap WKT message Deduplication |
+| `ENABLE_ODE_MAP_DEDUPLICATION` | `true` / `false` - Enable ODE MAP message Deduplication |
+| `ENABLE_ODE_TIM_DEDUPLICATION` | `true` / `false` - Enable ODE TIM message Deduplication |
+| `ENABLE_ODE_RAW_ENCODED_TIM_DEDUPLICATION` | `true` / `false` - Enable ODE Raw Encoded TIM Deduplication |
+| `ENABLE_PROCESSED_SPAT_DEDUPLICATION` | `true` / `false` - Enable ProcessedSpat Deduplication |
+| `ENABLE_ODE_BSM_DEDUPLICATION` | `true` / `false` - Enable ODE BSM Deduplication |
+
+### Generate a Github Token
+
+A GitHub token is required to pull artifacts from GitHub repositories. This is required to obtain the jpo-deduplicator jars and must be done before attempting to build this repository.
+
+1. Log into GitHub.
+2. Navigate to Settings -> Developer settings -> Personal access tokens.
+3. Click "New personal access token (classic)".
+ 1. As of now, GitHub does not support `Fine-grained tokens` for obtaining packages.
+4. Provide a name and expiration for the token.
+5. Select the `read:packages` scope.
+6. Click "Generate token" and copy the token.
+7. Copy the token name and token value into your `.env` file.
+
+For local development the following steps are also required
+8. Create a copy of [settings.xml](jpo-deduplicator/jpo-deduplicator/settings.xml) and save it to `~/.m2/settings.xml`
+9. Update the variables in your `~/.m2/settings.xml` with the token value and target jpo-ode organization.
+
+### Quick Run
+1. Create a copy of `sample.env` and rename it to `.env`.
+2. Update the variable `MAVEN_GITHUB_TOKEN` to a github token used for downloading jar file dependencies. For full instructions on how to generate a token please see here:
+3. Set the password for `MONGO_ADMIN_DB_PASS` and `MONGO_READ_WRITE_PASS` environmental variables to a secure password.
+4. Set the `COMPOSE_PROFILES` variable to: `kafka,kafka_ui,kafka_setup, jpo-deduplicator`
+5. Navigate back to the root directory and run the following command: `docker compose up -d`
+6. Produce a sample message to one of the sink topics by using `kafka_ui` by:
+ 1. Go to `localhost:8001`
+ 2. Click local -> Topics
+ 3. Select `topic.OdeMapJson`
+ 4. Select `Produce Message`
+ 5. Copy in sample JSON for a Map Message
+ 6. Click `Produce Message` multiple times
+7. View the synced message in `kafka_ui` by:
+ 1. Go to `localhost:8001`
+ 2. Click local -> Topics
+ 3. Select `topic.DeduplicatedOdeMapJson`
+ 4. You should now see only one copy of the map message sent.
+
[Back to top](#toc)
diff --git a/docker-compose-connect.yml b/docker-compose-connect.yml
index c80d7be..c78c49b 100644
--- a/docker-compose-connect.yml
+++ b/docker-compose-connect.yml
@@ -16,26 +16,31 @@ services:
memory: 4G
ports:
- "8083:8083"
+ healthcheck:
+ test: ["CMD", "curl", "-f", "http://localhost:8083/connectors"]
+ interval: 30s
+ timeout: 10s
+ retries: 4
depends_on:
mongo:
condition: service_healthy
+ kafka:
+ condition: service_healthy
environment:
- MONGO_URI: ${MONGO_URI}
- MONGO_DB_NAME: ${MONGO_DB_NAME}
CONNECT_BOOTSTRAP_SERVERS: ${KAFKA_BOOTSTRAP_SERVERS}
CONNECT_REST_ADVERTISED_HOST_NAME: connect
CONNECT_REST_PORT: 8083
CONNECT_GROUP_ID: kafka-connect-group
- CONNECT_CONFIG_STORAGE_TOPIC: topic.kafka-connect-configs
- CONNECT_CONFIG_STORAGE_REPLICATION_FACTOR: 1
- CONNECT_CONFIG_STORAGE_CLEANUP_POLICY: compact
+ # Topics are created with jikkou in the kafka-setup service
+ CONNECT_CONFIG_STORAGE_TOPIC: topic.KafkaConnectConfigs
+ CONNECT_CONFIG_STORAGE_REPLICATION_FACTOR: -1
CONNECT_OFFSET_FLUSH_INTERVAL_MS: 10000
- CONNECT_OFFSET_STORAGE_TOPIC: topic.kafka-connect-offsets
- CONNECT_OFFSET_STORAGE_REPLICATION_FACTOR: 1
+ CONNECT_OFFSET_STORAGE_TOPIC: topic.KafkaConnectOffsets
+ CONNECT_OFFSET_STORAGE_REPLICATION_FACTOR: -1
CONNECT_OFFSET_STORAGE_CLEANUP_POLICY: compact
- CONNECT_STATUS_STORAGE_TOPIC: topic.kafka-connect-status
+ CONNECT_STATUS_STORAGE_TOPIC: topic.KafkaConnectStatus
CONNECT_STATUS_STORAGE_CLEANUP_POLICY: compact
- CONNECT_STATUS_STORAGE_REPLICATION_FACTOR: 1
+ CONNECT_STATUS_STORAGE_REPLICATION_FACTOR: -1
CONNECT_KEY_CONVERTER: "org.apache.kafka.connect.json.JsonConverter"
CONNECT_VALUE_CONVERTER: "org.apache.kafka.connect.json.JsonConverter"
CONNECT_INTERNAL_KEY_CONVERTER: "org.apache.kafka.connect.json.JsonConverter"
@@ -43,5 +48,36 @@ services:
CONNECT_LOG4J_ROOT_LOGLEVEL: ${CONNECT_LOG_LEVEL}
CONNECT_LOG4J_LOGGERS: "org.apache.kafka.connect.runtime.rest=${CONNECT_LOG_LEVEL},org.reflections=${CONNECT_LOG_LEVEL},com.mongodb.kafka=${CONNECT_LOG_LEVEL}"
CONNECT_PLUGIN_PATH: /usr/share/confluent-hub-components
+
+ kafka-connect-setup:
+ profiles:
+ - all
+ - kafka_connect
+ - kafka_connect_standalone
+ image: jpo-jikkou
+ build:
+ context: jikkou
+ dockerfile: Dockerfile.jikkou
+ entrypoint: ./kafka_connector_init.sh
+ restart: on-failure
+ deploy:
+ resources:
+ limits:
+ cpus: '0.5'
+ memory: 1G
+ depends_on:
+ kafka-connect:
+ condition: service_healthy
+ environment:
+ CONNECT_URL: ${CONNECT_URL}
+ CONNECT_TASKS_MAX: ${CONNECT_TASKS_MAX}
+ CONNECT_CREATE_ODE: ${CONNECT_CREATE_ODE}
+ CONNECT_CREATE_GEOJSONCONVERTER: ${CONNECT_CREATE_GEOJSONCONVERTER}
+ CONNECT_CREATE_CONFLICTMONITOR: ${CONNECT_CREATE_CONFLICTMONITOR}
+ CONNECT_CREATE_DEDUPLICATOR: ${CONNECT_CREATE_DEDUPLICATOR}
+ MONGO_CONNECTOR_USERNAME: ${MONGO_ADMIN_DB_USER}
+ MONGO_CONNECTOR_PASSWORD: ${MONGO_ADMIN_DB_PASS:?}
+ MONGO_DB_IP: ${MONGO_IP}
+ MONGO_DB_NAME: ${MONGO_DB_NAME}
volumes:
- - ${CONNECT_SCRIPT_RELATIVE_PATH}:/scripts/connect_start.sh
\ No newline at end of file
+ - ${CONNECT_CONFIG_RELATIVE_PATH-./jikkou/kafka-connectors-values.yaml}:/app/kafka-connectors-values.yaml
\ No newline at end of file
diff --git a/docker-compose-deduplicator.yml b/docker-compose-deduplicator.yml
new file mode 100644
index 0000000..5c315d7
--- /dev/null
+++ b/docker-compose-deduplicator.yml
@@ -0,0 +1,44 @@
+services:
+ deduplicator:
+ profiles:
+ - all
+ - deduplicator
+ build:
+ context: jpo-deduplicator
+ dockerfile: Dockerfile
+ args:
+ MAVEN_GITHUB_TOKEN: ${MAVEN_GITHUB_TOKEN:?error}
+ MAVEN_GITHUB_ORG: ${MAVEN_GITHUB_ORG:?error}
+ image: jpo-deduplicator:latest
+ restart: ${RESTART_POLICY}
+ environment:
+ DOCKER_HOST_IP: ${DOCKER_HOST_IP}
+ KAFKA_BOOTSTRAP_SERVERS: ${KAFKA_BOOTSTRAP_SERVERS:?error}
+ spring.kafka.bootstrap-servers: ${KAFKA_BOOTSTRAP_SERVERS:?error}
+ enableProcessedMapDeduplication: ${ENABLE_PROCESSED_MAP_DEDUPLICATION}
+ enableProcessedMapWktDeduplication: ${ENABLE_PROCESSED_MAP_WKT_DEDUPLICATION}
+ enableOdeMapDeduplication: ${ENABLE_ODE_MAP_DEDUPLICATION}
+ enableOdeTimDeduplication: ${ENABLE_ODE_TIM_DEDUPLICATION}
+ enableOdeRawEncodedTimDeduplication: ${ENABLE_ODE_RAW_ENCODED_TIM_DEDUPLICATION}
+ enableProcessedSpatDeduplication: ${ENABLE_PROCESSED_SPAT_DEDUPLICATION}
+ enableOdeBsmDeduplication: ${ENABLE_ODE_BSM_DEDUPLICATION}
+
+
+
+ healthcheck:
+ test: ["CMD", "java", "-version"]
+ interval: 10s
+ timeout: 10s
+ retries: 20
+ logging:
+ options:
+ max-size: "10m"
+ max-file: "5"
+ deploy:
+ resources:
+ limits:
+ memory: 3G
+ depends_on:
+ kafka:
+ condition: service_healthy
+ required: false
diff --git a/docker-compose-kafka.yml b/docker-compose-kafka.yml
index 73aab7c..8d52a0a 100644
--- a/docker-compose-kafka.yml
+++ b/docker-compose-kafka.yml
@@ -37,6 +37,7 @@ services:
KAFKA_CFG_DELETE_TOPIC_ENABLE: "true"
KAFKA_CFG_LOG_RETENTION_HOURS: ${KAFKA_LOG_RETENTION_HOURS}
KAFKA_CFG_LOG_RETENTION_BYTES: ${KAFKA_LOG_RETENTION_BYTES}
+ KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE: "false"
logging:
options:
max-size: "10m"
@@ -48,11 +49,12 @@ services:
- all
- kafka_full
- kafka_setup
- image: kafka-setup
+ image: jpo-jikkou
build:
- context: kafka
+ context: jikkou
dockerfile: Dockerfile.jikkou
- restart: on-failure:3 # try up to 3 times
+ entrypoint: ./kafka_init.sh
+ restart: on-failure
deploy:
resources:
limits:
@@ -62,6 +64,7 @@ services:
kafka:
condition: service_healthy
environment:
+ KAFKA_BOOTSTRAP_SERVERS: ${KAFKA_BOOTSTRAP_SERVERS}
KAFKA_TOPIC_PARTITIONS: ${KAFKA_TOPIC_PARTITIONS}
KAFKA_TOPIC_REPLICAS: ${KAFKA_TOPIC_REPLICAS}
KAFKA_TOPIC_MIN_INSYNC_REPLICAS: ${KAFKA_TOPIC_MIN_INSYNC_REPLICAS}
@@ -71,7 +74,12 @@ services:
KAFKA_TOPIC_CREATE_GEOJSONCONVERTER: ${KAFKA_TOPIC_CREATE_GEOJSONCONVERTER}
KAFKA_TOPIC_CREATE_CONFLICTMONITOR: ${KAFKA_TOPIC_CREATE_CONFLICTMONITOR}
KAFKA_TOPIC_CREATE_DEDUPLICATOR: ${KAFKA_TOPIC_CREATE_DEDUPLICATOR}
-
+ MONGO_CONNECTOR_USERNAME: ${MONGO_ADMIN_DB_USER}
+ MONGO_CONNECTOR_PASSWORD: ${MONGO_ADMIN_DB_PASS}
+ MONGO_DB_IP: ${MONGO_IP}
+ MONGO_DB_NAME: ${MONGO_DB_NAME}
+ volumes:
+ - ${KAFKA_TOPIC_CONFIG_RELATIVE_PATH:-./jikkou/kafka-topics-values.yaml}:/app/kafka-topics-values.yaml
kafka-schema-registry:
profiles:
@@ -123,6 +131,8 @@ services:
DYNAMIC_CONFIG_ENABLED: 'true'
KAFKA_CLUSTERS_0_NAME: local
KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: ${KAFKA_BOOTSTRAP_SERVERS}
+ KAFKA_CLUSTERS_0_KAFKACONNECT_0_NAME: kafka-connect
+ KAFKA_CLUSTERS_0_KAFKACONNECT_0_ADDRESS: ${CONNECT_URL}
volumes:
kafka:
\ No newline at end of file
diff --git a/docker-compose-mongo.yml b/docker-compose-mongo.yml
index aefc4f9..2f72bcf 100644
--- a/docker-compose-mongo.yml
+++ b/docker-compose-mongo.yml
@@ -5,7 +5,7 @@ services:
- kafka_connect_standalone
- mongo_full
- mongo
- image: mongo:7
+ image: mongo:8
hostname: mongo
restart: ${RESTART_POLICY}
deploy:
@@ -17,24 +17,45 @@ services:
- "27017:27017"
environment:
MONGO_INITDB_ROOT_USERNAME: ${MONGO_ADMIN_DB_USER}
- MONGO_INITDB_ROOT_PASSWORD: ${MONGO_ADMIN_DB_PASS}
- MONGO_INITDB_DATABASE: admin
+ MONGO_INITDB_ROOT_PASSWORD: ${MONGO_ADMIN_DB_PASS:?}
+ MONGO_INITDB_DATABASE: admin
+ MONGO_DATABASE_STORAGE_COLLECTION_NAME: ${MONGO_DATABASE_STORAGE_COLLECTION_NAME}
+ MONGO_DATABASE_SIZE_GB: ${MONGO_DATABASE_SIZE_GB}
+ MONGO_DATABASE_SIZE_TARGET_PERCENT: ${MONGO_DATABASE_SIZE_TARGET_PERCENT}
+ MONGO_DATABASE_DELETE_THRESHOLD_PERCENT: ${MONGO_DATABASE_DELETE_THRESHOLD_PERCENT}
+ MONGO_DATABASE_MAX_TTL_RETENTION_SECONDS: ${MONGO_DATABASE_MAX_TTL_RETENTION_SECONDS}
+ MONGO_DATABASE_MIN_TTL_RETENTION_SECONDS: ${MONGO_DATABASE_MIN_TTL_RETENTION_SECONDS}
+ MONGO_ENABLE_STORAGE_RECORD: ${MONGO_ENABLE_STORAGE_RECORD}
+ MONGO_ENABLE_DYNAMIC_TTL: ${MONGO_ENABLE_DYNAMIC_TTL}
+ MONGO_DB_NAME: ${MONGO_DB_NAME}
+ MONGO_DB_KEYFILE_STRING: ${MONGO_DB_KEYFILE_STRING:?}
entrypoint:
- bash
- -c
- |
- openssl rand -base64 741 > /mongo_keyfile
- chmod 400 /mongo_keyfile
- chown 999:999 /mongo_keyfile
- exec docker-entrypoint.sh $$@
- command: "mongod --bind_ip_all --replSet rs0 --keyFile /mongo_keyfile"
+ apt update
+ apt install -y cron gettext systemctl dos2unix
+ systemctl start cron
+ systemctl enable cron
+ envsubst < /data/manage-volume-cron > /etc/cron.d/manage-volume-cron
+ dos2unix /etc/cron.d/manage-volume-cron
+ chmod 644 /etc/cron.d/manage-volume-cron
+ systemctl restart cron
+ echo "$MONGO_DB_KEYFILE_STRING" > /data/keyfile.txt
+ chmod 400 /data/keyfile.txt
+ chown 999:999 /data/keyfile.txt
+
+ exec docker-entrypoint.sh $$@
+ command: ["mongod", "--replSet", "rs0", "--bind_ip_all", "--keyFile", "/data/keyfile.txt"]
volumes:
- mongo_data:/data/db
+ - ./mongo/manage-volume-cron:/data/manage-volume-cron
+ - ./mongo/manage_volume.js:/data/manage_volume.js
healthcheck:
- test: |
- echo 'db.runCommand("ping").ok' | mongosh localhost:27017/test --quiet
+ test: "echo 'db.runCommand(\"ping\").ok' | mongosh --quiet --username ${MONGO_ADMIN_DB_USER} --password ${MONGO_ADMIN_DB_PASS} --authenticationDatabase admin --eval \"rs.status().ok\""
interval: 10s
- start_period: 30s
+ timeout: 10s
+ retries: 10
mongo-setup:
profiles:
@@ -42,7 +63,7 @@ services:
- kafka_connect_standalone
- mongo_full
- mongo
- image: mongo:7
+ image: mongo:8
hostname: mongo_setup
depends_on:
- mongo
@@ -54,15 +75,25 @@ services:
memory: 1G
environment:
MONGO_ADMIN_DB_USER: ${MONGO_ADMIN_DB_USER}
- MONGO_ADMIN_DB_PASS: ${MONGO_ADMIN_DB_PASS}
+ MONGO_ADMIN_DB_PASS: ${MONGO_ADMIN_DB_PASS:?}
MONGO_DB_NAME: ${MONGO_DB_NAME}
MONGO_READ_WRITE_USER: ${MONGO_READ_WRITE_USER}
- MONGO_READ_WRITE_PASS: ${MONGO_READ_WRITE_PASS}
- MONGO_COLLECTION_TTL: ${MONGO_COLLECTION_TTL}
+ MONGO_READ_WRITE_PASS: ${MONGO_READ_WRITE_PASS:?}
+ MONGO_READ_USER: ${MONGO_READ_USER}
+ MONGO_READ_PASS: ${MONGO_READ_PASS:?}
+ MONGO_EXPORTER_USERNAME: ${MONGO_EXPORTER_USERNAME}
+ MONGO_EXPORTER_PASSWORD: ${MONGO_EXPORTER_PASSWORD:?}
+ MONGO_DATA_RETENTION_SECONDS: ${MONGO_DATA_RETENTION_SECONDS}
+ MONGO_ASN_RETENTION_SECONDS: ${MONGO_ASN_RETENTION_SECONDS}
+ CONNECT_CREATE_GEOJSONCONVERTER: ${CONNECT_CREATE_GEOJSONCONVERTER}
+ CONNECT_CREATE_CONFLICTMONITOR: ${CONNECT_CREATE_CONFLICTMONITOR}
+ CONNECT_CREATE_DEDUPLICATOR: ${CONNECT_CREATE_DEDUPLICATOR}
+
entrypoint: ["/bin/bash", "setup_mongo.sh"]
volumes:
- ${MONGO_SETUP_SCRIPT_RELATIVE_PATH}:/setup_mongo.sh
- ${MONGO_CREATE_INDEXES_SCRIPT_RELATIVE_PATH}:/create_indexes.js
+ - ${MONGO_INIT_REPLICAS_SCRIPT_RELATIVE_PATH}:/init_replicas.js
mongo-express:
profiles:
@@ -82,12 +113,12 @@ services:
depends_on:
- mongo
environment:
- ME_CONFIG_MONGODB_SERVER: "mongo"
ME_CONFIG_MONGODB_ENABLE_ADMIN: "true"
ME_CONFIG_BASICAUTH_USERNAME: ${MONGO_EXPRESS_USER}
- ME_CONFIG_BASICAUTH_PASSWORD: ${MONGO_EXPRESS_PASS}
+ ME_CONFIG_BASICAUTH_PASSWORD: ${MONGO_EXPRESS_PASS:?}
ME_CONFIG_MONGODB_ADMINUSERNAME: ${MONGO_ADMIN_DB_USER}
- ME_CONFIG_MONGODB_ADMINPASSWORD: ${MONGO_ADMIN_DB_PASS}
+ ME_CONFIG_MONGODB_ADMINPASSWORD: ${MONGO_ADMIN_DB_PASS:?}
+ ME_CONFIG_MONGODB_URL: mongodb://${MONGO_ADMIN_DB_USER}:${MONGO_ADMIN_DB_PASS}@${MONGO_IP}:27017/?authSource=admin&directConnection=true
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8081"]
interval: 30s
diff --git a/docker-compose.yml b/docker-compose.yml
index 4410b10..c0719e5 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,4 +1,5 @@
include:
- docker-compose-connect.yml
- docker-compose-mongo.yml
- - docker-compose-kafka.yml
\ No newline at end of file
+ - docker-compose-kafka.yml
+ - docker-compose-deduplicator.yml
\ No newline at end of file
diff --git a/kafka/Dockerfile.jikkou b/jikkou/Dockerfile.jikkou
similarity index 53%
rename from kafka/Dockerfile.jikkou
rename to jikkou/Dockerfile.jikkou
index 139b4a1..17bb27d 100644
--- a/kafka/Dockerfile.jikkou
+++ b/jikkou/Dockerfile.jikkou
@@ -1,16 +1,26 @@
-FROM streamthoughts/jikkou:0.35.2
+FROM streamthoughts/jikkou:0.35.3
# Root user is required to run the 'jikkou apply' command in the kafka_init.sh script
USER root
+# install yq for kafka connect status check
+RUN apk add yq
+
COPY ./application.conf /app/application.conf
COPY ./jikkouconfig /etc/jikkou/config
+
+# kafka topic creation files
COPY ./kafka-topics-template.jinja /app/kafka-topics-template.jinja
-COPY ./kafka-topics-values.yaml /app/kafka-topics-values.yaml
COPY ./kafka_init.sh /app/kafka_init.sh
+# kafka connect creation files
+COPY ./kafka-connectors-template.jinja /app/kafka-connectors-template.jinja
+COPY ./kafka_connector_init.sh /app/kafka_connector_init.sh
+
# Create/update topics then exit container
-ENTRYPOINT ./kafka_init.sh
+# Disabled by default to be applied in the docker-compose.yml instead
+# ENTRYPOINT ./kafka_init.sh
+# ENTRYPOINT ./kafka_connector_init.sh
## For dev & testing, uncomment to keep the container running to be be able to
## use the Jikkou command line within Docker Desktop:
diff --git a/kafka/application.conf b/jikkou/application.conf
similarity index 98%
rename from kafka/application.conf
rename to jikkou/application.conf
index 9956fce..54635df 100644
--- a/kafka/application.conf
+++ b/jikkou/application.conf
@@ -22,7 +22,7 @@ jikkou {
# The default Kafka Client configuration
client {
bootstrap.servers = "kafka:9092"
- bootstrap.servers = ${?JIKKOU_DEFAULT_KAFKA_BOOTSTRAP_SERVERS}
+ bootstrap.servers = ${?KAFKA_BOOTSTRAP_SERVERS}
# security.protocol = "SSL"
# ssl.keystore.location = "/tmp/client.keystore.p12"
# ssl.keystore.password = "password"
@@ -55,6 +55,7 @@ jikkou {
name = "kafka-connect"
# URL of the Kafka Connect service
url = "http://kafka-connect:8083"
+ url = ${?CONNECT_URL}
# Method to use for authenticating on Kafka Connect. Available values are: [none, basicauth, ssl]
authMethod = none
# Use when 'authMethod' is 'basicauth' to specify the username for Authorization Basic header
diff --git a/kafka/jikkouconfig b/jikkou/jikkouconfig
similarity index 100%
rename from kafka/jikkouconfig
rename to jikkou/jikkouconfig
diff --git a/jikkou/kafka-connectors-template.jinja b/jikkou/kafka-connectors-template.jinja
new file mode 100644
index 0000000..8f34ca2
--- /dev/null
+++ b/jikkou/kafka-connectors-template.jinja
@@ -0,0 +1,72 @@
+{# ----------------- Create Kafka Connectors to MongoDB per app ----------------- #}
+{% macro create_connector(app) %}
+
+{# Table Topics #}
+{% for connector in app.connectors %}
+---
+apiVersion: "kafka.jikkou.io/v1beta1" {# The api version (required) #}
+kind: "KafkaConnector" {# The resource kind (required) #}
+metadata:
+ name: sink.{{ connector.connectorName | default(connector.collectionName) }}
+ labels:
+ kafka.jikkou.io/connect-cluster: {{ values.clusterName }}
+spec:
+ connectorClass: {{ system.env.KAFKA_CONNECT_CONNECTOR_CLASS | default(values.connectorClass) }}
+ tasksMax: {{ system.env.KAFKA_TOPIC_PARTITIONS | default(values.tasksMax) }}
+ config:
+ collection: {{ connector.collectionName }}
+ connection.uri: mongodb://{{ system.env.MONGO_CONNECTOR_USERNAME }}:{{ system.env.MONGO_CONNECTOR_PASSWORD }}@{{ system.env.MONGO_DB_IP }}:27017/
+ connector.class: com.mongodb.kafka.connect.MongoSinkConnector
+ database: {{ system.env.MONGO_DB_NAME }}
+ errors.log.enable: false
+ errors.log.include.messages: false
+ errors.tolerance: all
+ group.id: connector-consumer
+ key.converter: org.apache.kafka.connect.storage.StringConverter
+ key.converter.schemas.enable: false
+ mongo.errors.tolerance: all
+ topics: {{ connector.topicName }}
+ value.converter: org.apache.kafka.connect.json.JsonConverter
+ value.converter.schemas.enable: false
+
+ {%+ if connector.generateTimestamp %}transforms: AddTimestamp,AddedTimestampConverter{% endif %}
+ {%+ if connector.generateTimestamp %}transforms.AddedTimestampConverter.field: recordGeneratedAt{% endif %}
+ {%+ if connector.generateTimestamp %}transforms.AddedTimestampConverter.target.type: Timestamp{% endif %}
+ {%+ if connector.generateTimestamp %}transforms.AddedTimestampConverter.type: org.apache.kafka.connect.transforms.TimestampConverter$Value{% endif %}
+ {%+ if connector.generateTimestamp %}transforms.AddTimestamp.timestamp.field: recordGeneratedAt{% endif %}
+ {%+ if connector.generateTimestamp %}transforms.AddTimestamp.type: org.apache.kafka.connect.transforms.InsertField$Value{% endif %}
+
+ {%+ if connector.useTimestamp %}transforms: TimestampConverter{% endif %}
+ {%+ if connector.useTimestamp %}transforms.TimestampConverter.field: {{ connector.timestampField }}{% endif %}
+ {%+ if connector.useTimestamp %}transforms.TimestampConverter.type: org.apache.kafka.connect.transforms.TimestampConverter$Value{% endif %}
+ {%+ if connector.useTimestamp %}transforms.TimestampConverter.target.type: Timestamp{% endif %}
+
+ {%+ if connector.useKey %}document.id.strategy: com.mongodb.kafka.connect.sink.processor.id.strategy.PartialValueStrategy{% endif %}
+ {%+ if connector.useKey %}document.id.strategy.partial.value.projection.list: {{ connector.keyField }}{% endif %}
+ {%+ if connector.useKey %}document.id.strategy.partial.value.projection.type: AllowList{% endif %}
+ {%+ if connector.useKey %}document.id.strategy.overwrite.existing: true{% endif %}
+
+ state: "RUNNING"
+{% endfor %}
+
+{% endmacro %}
+
+{#------- Create topics for apps with env variable = true ----------#}
+{% if system.env.CONNECT_CREATE_ODE %}
+{{ create_connector(values.apps.ode) }}
+{% endif %}
+
+{% if system.env.CONNECT_CREATE_GEOJSONCONVERTER %}
+{{ create_connector(values.apps.geojsonconverter) }}
+{% endif %}
+
+{% if system.env.CONNECT_CREATE_CONFLICTMONITOR %}
+{{ create_connector(values.apps.conflictmonitor) }}
+{% endif %}
+
+{% if system.env.CONNECT_CREATE_DEDUPLICATOR %}
+{{ create_connector(values.apps.deduplicator) }}
+{% else %}
+{{ create_connector(values.apps.ode_duplicated) }}
+{{ create_connector(values.apps.geojsonconverter_duplicated) }}
+{% endif %}
\ No newline at end of file
diff --git a/jikkou/kafka-connectors-values.yaml b/jikkou/kafka-connectors-values.yaml
new file mode 100644
index 0000000..76daffe
--- /dev/null
+++ b/jikkou/kafka-connectors-values.yaml
@@ -0,0 +1,353 @@
+#======================================================================================
+# Kafka Connectors configuration settings
+#======================================================================================
+
+#--------------------------------------------------------------------------------------
+# Configuration parameters used to create kafka connectors to sync data from Kafka topics
+# to MongoDB collections.
+#
+# Ref: https://docs.confluent.io/platform/current/connect/index.html#connect-connectors
+#--------------------------------------------------------------------------------------
+
+clusterName: "kafka-connect"
+connectorClass: "com.mongodb.kafka.connect.MongoSinkConnector"
+tasksMax: 1
+
+
+#--------------------------------------------------------------------------------------
+# Kafka Connectors are grouped by application. Apps with the corresponding environment variable
+# equal to true are create or updated.
+# - ode
+# - geojsonconverter
+# - conflictmonitor
+#
+# Some Kafka topics have a non-duplicated topic and a duplicated topic. The duplicated topic will
+# used if the deduplicator is not enabled.
+# IF env var: KAFKA_TOPIC_CREATE_DEDUPLICATOR = true
+# - deduplicator
+# ELSE
+# - ode_duplicated
+# - geojsonconverter_duplicated
+#
+# The Kafka Connectors have the following configuration settings:
+# Required settings:
+# - topicName: The name of the Kafka topic to read from
+# - collectionName: The name of the MongoDB collection to write to
+# Optional settings:
+# - generateTimestamp: If true, the connector will add a timestamp field to the document
+# - connectorName: The name of the connector
+# - useTimestamp: converts the "timestampField" field at the top level of the value to a BSON date
+# - timestampField: The name of the timestamp field
+# - useKey: If true, the connector will use the "keyField" as the document _id in MongoDB
+# - keyField: The name of the key field
+#
+#--------------------------------------------------------------------------------------
+apps:
+ ode:
+ name: jpo-ode
+ connectors:
+ - topicName: topic.OdeRawEncodedBSMJson
+ collectionName: OdeRawEncodedBSMJson
+ generateTimestamp: true
+ - topicName: topic.OdeRawEncodedMAPJson
+ collectionName: OdeRawEncodedMAPJson
+ generateTimestamp: true
+ - topicName: topic.OdeRawEncodedSPATJson
+ collectionName: OdeRawEncodedSPATJson
+ generateTimestamp: true
+ - topicName: topic.OdeSpatJson
+ collectionName: OdeSpatJson
+ generateTimestamp: true
+ - topicName: topic.OdeTimJsonTMCFiltered
+ collectionName: OdeTimJsonTMCFiltered
+ generateTimestamp: true
+ - topicName: topic.OdeTimBroadcastJson
+ collectionName: OdeTimBroadcastJson
+ generateTimestamp: true
+ - topicName: topic.OdeTIMCertExpirationTimeJson
+ collectionName: OdeTIMCertExpirationTimeJson
+ generateTimestamp: true
+ - topicName: topic.OdeRawEncodedPSMJson
+ collectionName: OdeRawEncodedPSMJson
+ generateTimestamp: true
+ - topicName: topic.OdePsmJson
+ collectionName: OdePsmJson
+ generateTimestamp: true
+ - topicName: topic.OdeRawEncodedSRMJson
+ collectionName: OdeRawEncodedSRMJson
+ generateTimestamp: true
+ - topicName: topic.OdeSrmJson
+ collectionName: OdeSrmJson
+ generateTimestamp: true
+ - topicName: topic.OdeRawEncodedSSMJson
+ collectionName: OdeRawEncodedSSMJson
+ generateTimestamp: true
+ - topicName: topic.OdeSsmJson
+ collectionName: OdeSsmJson
+ generateTimestamp: true
+ - topicName: topic.OdeDriverAlertJson
+ collectionName: OdeDriverAlertJson
+ generateTimestamp: true
+ ode_duplicated:
+ name: ode-duplicated
+ connectors:
+ - topicName: topic.OdeMapJson
+ collectionName: OdeMapJson
+ generateTimestamp: true
+ - topicName: topic.OdeTimJson
+ collectionName: OdeTimJson
+ generateTimestamp: true
+ - topicName: topic.OdeBsmJson
+ collectionName: OdeBsmJson
+ generateTimestamp: true
+ - topicName: topic.OdeRawEncodedTIMJson
+ collectionName: OdeRawEncodedTIMJson
+ generateTimestamp: true
+ geojsonconverter:
+ name: geojsonconverter
+ connectors:
+ - topicName: topic.ProcessedBsm
+ collectionName: ProcessedBsm
+ generateTimestamp: true
+ geojsonconverter_duplicated:
+ name: geojsonconverter-duplicated
+ connectors:
+ - topicName: topic.ProcessedMap
+ collectionName: ProcessedMap
+ generateTimestamp: true
+ - topicName: topic.ProcessedSpat
+ collectionName: ProcessedSpat
+ generateTimestamp: true
+ deduplicator:
+ name: deduplicator
+ connectors:
+ - topicName: topic.DeduplicatedProcessedMap
+ collectionName: ProcessedMap
+ generateTimestamp: true
+ connectorName: DeduplicatedProcessedMap
+ - topicName: topic.DeduplicatedOdeMapJson
+ collectionName: OdeMapJson
+ generateTimestamp: true
+ connectorName: DeduplicatedOdeMapJson
+ - topicName: topic.DeduplicatedOdeTimJson
+ collectionName: OdeTimJson
+ generateTimestamp: true
+ connectorName: DeduplicatedOdeTimJson
+ - topicName: topic.DeduplicatedOdeRawEncodedTIMJson
+ collectionName: OdeRawEncodedTIMJson
+ generateTimestamp: true
+ connectorName: DeduplicatedOdeRawEncodedTIMJson
+ - topicName: topic.DeduplicatedOdeBsmJson
+ collectionName: OdeBsmJson
+ generateTimestamp: true
+ connectorName: DeduplicatedOdeBsmJson
+ - topicName: topic.DeduplicatedProcessedSpat
+ collectionName: ProcessedSpat
+ generateTimestamp: true
+ connectorName: DeduplicatedProcessedSpat
+ conflictmonitor:
+ name: conflictmonitor
+ connectors:
+ # Record Events
+ - topicName: topic.CmStopLinePassageEvent
+ collectionName: CmStopLinePassageEvent
+ useTimestamp: true
+ timestampField: eventGeneratedAt
+ - topicName: topic.CmStopLineStopEvent
+ collectionName: CmStopLineStopEvent
+ useTimestamp: true
+ timestampField: eventGeneratedAt
+ - topicName: topic.CmSignalStateConflictEvents
+ collectionName: CmSignalStateConflictEvents
+ useTimestamp: true
+ timestampField: eventGeneratedAt
+ - topicName: topic.CmIntersectionReferenceAlignmentEvents
+ collectionName: CmIntersectionReferenceAlignmentEvents
+ useTimestamp: true
+ timestampField: eventGeneratedAt
+ - topicName: topic.CmSignalGroupAlignmentEvents
+ collectionName: CmSignalGroupAlignmentEvents
+ useTimestamp: true
+ timestampField: eventGeneratedAt
+ - topicName: topic.CmConnectionOfTravelEvent
+ collectionName: CmConnectionOfTravelEvent
+ useTimestamp: true
+ timestampField: eventGeneratedAt
+ - topicName: topic.CmLaneDirectionOfTravelEvent
+ collectionName: CmLaneDirectionOfTravelEvent
+ useTimestamp: true
+ timestampField: eventGeneratedAt
+ - topicName: topic.CmSpatTimeChangeDetailsEvent
+ collectionName: CmSpatTimeChangeDetailsEvent
+ useTimestamp: true
+ timestampField: eventGeneratedAt
+ - topicName: topic.CmSpatMinimumDataEvents
+ collectionName: CmSpatMinimumDataEvents
+ useTimestamp: true
+ timestampField: eventGeneratedAt
+ - topicName: topic.CmMapBroadcastRateEvents
+ collectionName: CmMapBroadcastRateEvents
+ useTimestamp: true
+ timestampField: eventGeneratedAt
+ - topicName: topic.CmMapMinimumDataEvents
+ collectionName: CmMapMinimumDataEvents
+ useTimestamp: true
+ timestampField: eventGeneratedAt
+ - topicName: topic.CmSpatBroadcastRateEvents
+ collectionName: CmSpatBroadcastRateEvents
+ useTimestamp: true
+ timestampField: eventGeneratedAt
+ - topicName: topic.CmTimestampDeltaEvent
+ collectionName: CmTimestampDeltaEvent
+ useTimestamp: true
+ timestampField: eventGeneratedAt
+ - topicName: topic.CmSpatMessageCountProgressionEvents
+ collectionName: CmSpatMessageCountProgressionEvents
+ useTimestamp: true
+ timestampField: eventGeneratedAt
+ - topicName: topic.CmMapMessageCountProgressionEvents
+ collectionName: CmMapMessageCountProgressionEvents
+ useTimestamp: true
+ timestampField: eventGeneratedAt
+ - topicName: topic.CmBsmMessageCountProgressionEvents
+ collectionName: CmBsmMessageCountProgressionEvents
+ useTimestamp: true
+ timestampField: eventGeneratedAt
+ - topicName: topic.CmSpatMinimumDataEventAggregation
+ collectionName: CmSpatMinimumDataEventAggregation
+ generateTimestamp: true
+ timestampField: eventGeneratedAt
+ - topicName: topic.CmMapMinimumDataEventAggregation
+ collectionName: CmMapMinimumDataEventAggregation
+ generateTimestamp: true
+ timestampField: eventGeneratedAt
+ - topicName: topic.CmIntersectionReferenceAlignmentEventAggregation
+ collectionName: CmIntersectionReferenceAlignmentEventAggregation
+ generateTimestamp: true
+ timestampField: eventGeneratedAt
+ - topicName: topic.CmSignalGroupAlignmentEventAggregation
+ collectionName: CmSignalGroupAlignmentEventAggregation
+ generateTimestamp: true
+ timestampField: eventGeneratedAt
+ - topicName: topic.CmSignalStateConflictEventAggregation
+ collectionName: CmSignalStateConflictEventAggregation
+ generateTimestamp: true
+ timestampField: eventGeneratedAt
+ - topicName: topic.CmSpatTimeChangeDetailsEventAggregation
+ collectionName: CmSpatTimeChangeDetailsEventAggregation
+ generateTimestamp: true
+ timestampField: eventGeneratedAt
+ - topicName: topic.CmEventStateProgressionEventAggregation
+ collectionName: CmEventStateProgressionEventAggregation
+ generateTimestamp: true
+ timestampField: eventGeneratedAt
+ - topicName: topic.CmBsmMessageCountProgressionEventAggregation
+ collectionName: CmBsmMessageCountProgressionEventAggregation
+ generateTimestamp: true
+ timestampField: eventGeneratedAt
+ - topicName: topic.CmMapMessageCountProgressionEventAggregation
+ collectionName: CmMapMessageCountProgressionEventAggregation
+ generateTimestamp: true
+ timestampField: eventGeneratedAt
+ - topicName: topic.CmSpatMessageCountProgressionEventAggregation
+ collectionName: CmSpatMessageCountProgressionEventAggregation
+ generateTimestamp: true
+ timestampField: eventGeneratedAt
+
+ # Record BSM events:
+ - topicName: topic.CmBsmEvents
+ collectionName: CmBsmEvents
+ generateTimestamp: true
+
+ # Record Assessments:
+ - topicName: topic.CmLaneDirectionOfTravelAssessment
+ collectionName: CmLaneDirectionOfTravelAssessment
+ useTimestamp: true
+ timestampField: assessmentGeneratedAt
+ - topicName: topic.CmConnectionOfTravelAssessment
+ collectionName: CmConnectionOfTravelAssessment
+ useTimestamp: true
+ timestampField: assessmentGeneratedAt
+ - topicName: topic.CmSignalStateEventAssessment
+ collectionName: CmSignalStateEventAssessment
+ useTimestamp: true
+ timestampField: assessmentGeneratedAt
+ - topicName: topic.CmStopLineStopAssessment
+ collectionName: CmStopLineStopAssessment
+ useTimestamp: true
+ timestampField: assessmentGeneratedAt
+
+ # Record Notifications
+ - topicName: topic.CmSpatTimeChangeDetailsNotification
+ collectionName: CmSpatTimeChangeDetailsNotification
+ useTimestamp: true
+ timestampField: notificationGeneratedAt
+ - topicName: topic.CmLaneDirectionOfTravelNotification
+ collectionName: CmLaneDirectionOfTravelNotification
+ useTimestamp: true
+ timestampField: notificationGeneratedAt
+ - topicName: topic.CmConnectionOfTravelNotification
+ collectionName: CmConnectionOfTravelNotification
+ useTimestamp: true
+ timestampField: notificationGeneratedAt
+ - topicName: topic.CmAppHealthNotifications
+ collectionName: CmAppHealthNotifications
+ useTimestamp: true
+ timestampField: notificationGeneratedAt
+ - topicName: topic.CmSignalStateConflictNotification
+ collectionName: CmSignalStateConflictNotification
+ useTimestamp: true
+ timestampField: notificationGeneratedAt
+ - topicName: topic.CmSignalGroupAlignmentNotification
+ collectionName: CmSignalGroupAlignmentNotification
+ useTimestamp: true
+ timestampField: notificationGeneratedAt
+ - topicName: topic.CmNotification
+ collectionName: CmNotification
+ useTimestamp: true
+ timestampField: notificationGeneratedAt
+ useKey: true
+ keyField: key
+ - topicName: topic.CmStopLineStopNotification
+ collectionName: CmStopLineStopNotification
+ useTimestamp: true
+ timestampField: notificationGeneratedAt
+ useKey: true
+ keyField: key
+ - topicName: topic.CmStopLinePassageNotification
+ collectionName: CmStopLinePassageNotification
+ useTimestamp: true
+ timestampField: notificationGeneratedAt
+ useKey: true
+ keyField: key
+ - topicName: topic.CmTimestampDeltaNotification
+ collectionName: CmTimestampDeltaNotification
+ useTimestamp: true
+ timestampField: notificationGeneratedAt
+ useKey: true
+ keyField: key
+ - topicName: topic.CmEventStateProgressionNotification
+ collectionName: CmEventStateProgressionNotification
+ generateTimestamp: true
+ timestampField: eventGeneratedAt
+ - topicName: topic.CmIntersectionReferenceAlignmentNotificationAggregation
+ collectionName: CmIntersectionReferenceAlignmentNotificationAggregation
+ generateTimestamp: true
+ timestampField: notificationGeneratedAt
+ - topicName: topic.CmSignalGroupAlignmentNotificationAggregation
+ collectionName: CmSignalGroupAlignmentNotificationAggregation
+ generateTimestamp: true
+ timestampField: notificationGeneratedAt
+ - topicName: topic.CmSignalStateConflictNotificationAggregation
+ collectionName: CmSignalStateConflictNotificationAggregation
+ generateTimestamp: true
+ timestampField: notificationGeneratedAt
+ - topicName: topic.CmSpatTimeChangeDetailsNotificationAggregation
+ collectionName: CmSpatTimeChangeDetailsNotificationAggregation
+ generateTimestamp: true
+ timestampField: notificationGeneratedAt
+ - topicName: topic.CmEventStateProgressionNotificationAggregation
+ collectionName: CmEventStateProgressionNotificationAggregation
+ generateTimestamp: true
+ timestampField: notificationGeneratedAt
+
diff --git a/kafka/kafka-topics-template.jinja b/jikkou/kafka-topics-template.jinja
similarity index 64%
rename from kafka/kafka-topics-template.jinja
rename to jikkou/kafka-topics-template.jinja
index 27f348e..e2d1ebc 100644
--- a/kafka/kafka-topics-template.jinja
+++ b/jikkou/kafka-topics-template.jinja
@@ -2,7 +2,7 @@
{% macro create_topics(app) %}
{# Stream Topics #}
-{% for topicName in app.streamTopics %}
+{% for topicName in app.streamTopics | default([]) %}
---
apiVersion: "kafka.jikkou.io/v1beta2"
kind: KafkaTopic
@@ -20,7 +20,7 @@ spec:
{% endfor %}
{# Table Topics #}
-{% for topicName in app.tableTopics %}
+{% for topicName in app.tableTopics | default([]) %}
---
apiVersion: "kafka.jikkou.io/v1beta2"
kind: KafkaTopic
@@ -37,8 +37,28 @@ spec:
delete.retention.ms: {{ system.env.KAFKA_TOPIC_DELETE_RETENTION_MS | default(values.deleteRetentionMs) }}
{% endfor %}
+{# macro not needed at the moment but it allows for more custom topic creation #}
+{% for topic in app.customTopics | default([]) %}
+---
+apiVersion: "kafka.jikkou.io/v1beta2"
+kind: KafkaTopic
+metadata:
+ name: "{{ topic.topicName }}"
+ labels:
+ app: "{{ app.name }}"
+spec:
+ partitions: {{ topic.partitions | default(values.partitions) }}
+ replicas: {{ system.env.KAFKA_TOPIC_REPLICAS | default(values.replicas) }}
+ configs:
+ cleanup.policy: {{ topic.cleanUpPolicy | default(delete) }}
+ min.insync.replicas: {{ system.env.KAFKA_TOPIC_MIN_INSYNC_REPLICAS | default(values.minInsyncReplicas) }}
+ delete.retention.ms: {{ system.env.KAFKA_TOPIC_DELETE_RETENTION_MS | default(values.deleteRetentionMs) }}
+{% endfor %}
+
{% endmacro %}
+{{ create_topics(values.apps.kafkaconnect) }}
+
{#------- Create topics for apps with env variable = true ----------#}
{% if system.env.KAFKA_TOPIC_CREATE_ODE %}
{{ create_topics(values.apps.ode) }}
@@ -54,7 +74,4 @@ spec:
{% if system.env.KAFKA_TOPIC_CREATE_DEDUPLICATOR %}
{{ create_topics(values.apps.deduplicator) }}
-{% endif %}
-
-
-
+{% endif %}
\ No newline at end of file
diff --git a/kafka/kafka-topics-values.yaml b/jikkou/kafka-topics-values.yaml
similarity index 78%
rename from kafka/kafka-topics-values.yaml
rename to jikkou/kafka-topics-values.yaml
index f60bdf1..0ddb483 100644
--- a/kafka/kafka-topics-values.yaml
+++ b/jikkou/kafka-topics-values.yaml
@@ -59,6 +59,7 @@ apps:
- topic.OdeTimJsonTMCFiltered
- topic.OdeTimBroadcastJson
- topic.J2735TimBroadcastJson
+ - topic.FilteredOdeTimJson
- topic.OdeDriverAlertJson
- topic.Asn1DecoderInput
- topic.Asn1DecoderOutput
@@ -82,6 +83,7 @@ apps:
- topic.OdePsmTxPojo
- topic.OdePsmJson
tableTopics: {}
+ customTopics: {}
geojsonconverter:
name: jpo-geojsonconverter
streamTopics:
@@ -90,6 +92,7 @@ apps:
- topic.ProcessedMapWKT
- topic.ProcessedBsm
tableTopics: {}
+ customTopics: {}
conflictmonitor:
name: jpo-conflictmonitor
streamTopics:
@@ -111,6 +114,16 @@ apps:
- topic.CmBsmIntersection
- topic.CmKafkaStateChangeEvents
- topic.CmTimestampDeltaEvent
+ - topic.CmSpatMinimumDataEventAggregation
+ - topic.CmMapMinimumDataEventAggregation
+ - topic.CmIntersectionReferenceAlignmentEventAggregation
+ - topic.CmSignalGroupAlignmentEventAggregation
+ - topic.CmSignalStateConflictEventAggregation
+ - topic.CmSpatTimeChangeDetailsEventAggregation
+ - topic.CmEventStateProgressionEventAggregation
+ - topic.CmBsmMessageCountProgressionEventAggregation
+ - topic.CmMapMessageCountProgressionEventAggregation
+ - topic.CmSpatMessageCountProgressionEventAggregation
tableTopics:
- topic.CmLaneDirectionOfTravelNotification
- topic.CmConnectionOfTravelNotification
@@ -137,6 +150,12 @@ apps:
- topic.CmSpatRevisionCounterEvents
- topic.CmBsmRevisionCounterEvents
- topic.CmTimestampDeltaNotification
+ - topic.CmIntersectionReferenceAlignmentNotificationAggregation
+ - topic.CmSignalGroupAlignmentNotificationAggregation
+ - topic.CmSignalStateConflictNotificationAggregation
+ - topic.CmSpatTimeChangeDetailsNotificationAggregation
+ - topic.CmEventStateProgressionNotificationAggregation
+ customTopics: {}
deduplicator:
name: jpo-deduplicator
streamTopics:
@@ -146,4 +165,19 @@ apps:
- topic.DeduplicatedOdeTimJson
- topic.DeduplicatedOdeRawEncodedTIMJson
- topic.DeduplicatedOdeBsmJson
- tableTopics: {}
\ No newline at end of file
+ tableTopics: {}
+ customTopics: {}
+ kafkaconnect:
+ name: jpo-kafka-connect
+ streamTopics: {}
+ tableTopics: {}
+ customTopics:
+ - topicName: topic.KafkaConnectConfigs
+ partitions: 1
+ cleanUpPolicy: compact
+ - topicName: topic.KafkaConnectOffsets
+ partitions: 20
+ cleanUpPolicy: compact
+ - topicName: topic.KafkaConnectStatus
+ partitions: 10
+ cleanUpPolicy: compact
\ No newline at end of file
diff --git a/jikkou/kafka_connector_init.sh b/jikkou/kafka_connector_init.sh
new file mode 100755
index 0000000..c9e13eb
--- /dev/null
+++ b/jikkou/kafka_connector_init.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+echo "CONNECT_CREATE_ODE=$CONNECT_CREATE_ODE"
+echo "CONNECT_CREATE_GEOJSONCONVERTER=$CONNECT_CREATE_GEOJSONCONVERTER"
+echo "CONNECT_CREATE_CONFLICTMONITOR=$CONNECT_CREATE_CONFLICTMONITOR"
+echo "CONNECT_CREATE_DEDUPLICATOR=$CONNECT_CREATE_DEDUPLICATOR"
+
+# Set the maximum number of retries
+MAX_RETRIES=5
+RETRY_COUNT=0
+
+# Retry the health check until it is ready or the retry limit is reached
+until ./jikkou health get kafkaconnect | yq -e '.status.name == "UP"' > /dev/null; do
+ echo "Waiting 10 sec for Kafka Connect to be ready (Attempt: $((RETRY_COUNT+1))/$MAX_RETRIES)"
+ RETRY_COUNT=$((RETRY_COUNT+1))
+ sleep 10
+done
+
+./jikkou validate \
+ --files kafka-connectors-template.jinja \
+ --values-files kafka-connectors-values.yaml
+
+./jikkou apply \
+ --files kafka-connectors-template.jinja \
+ --values-files kafka-connectors-values.yaml
\ No newline at end of file
diff --git a/kafka/kafka_init.sh b/jikkou/kafka_init.sh
old mode 100644
new mode 100755
similarity index 84%
rename from kafka/kafka_init.sh
rename to jikkou/kafka_init.sh
index aa4669d..ed21935
--- a/kafka/kafka_init.sh
+++ b/jikkou/kafka_init.sh
@@ -12,8 +12,5 @@ echo "KAFKA_TOPIC_CREATE_DEDUPLICATOR=$KAFKA_TOPIC_CREATE_DEDUPLICATOR"
# Create or update topics
./jikkou apply \
- --files kafka-topics-template.jinja \
- --values-files kafka-topics-values.yaml
-
-
-
+ --files kafka-topics-template.jinja \
+ --values-files kafka-topics-values.yaml
\ No newline at end of file
diff --git a/jpo-deduplicator/.dockerignore b/jpo-deduplicator/.dockerignore
new file mode 100644
index 0000000..1ace176
--- /dev/null
+++ b/jpo-deduplicator/.dockerignore
@@ -0,0 +1 @@
+jpo-deduplicator/target/
\ No newline at end of file
diff --git a/jpo-deduplicator/Dockerfile b/jpo-deduplicator/Dockerfile
new file mode 100644
index 0000000..6228e45
--- /dev/null
+++ b/jpo-deduplicator/Dockerfile
@@ -0,0 +1,57 @@
+FROM maven:3.8-eclipse-temurin-21-alpine AS builder
+
+WORKDIR /home
+
+ARG MAVEN_GITHUB_TOKEN
+ARG MAVEN_GITHUB_ORG
+
+ENV MAVEN_GITHUB_TOKEN=$MAVEN_GITHUB_TOKEN
+ENV MAVEN_GITHUB_ORG=$MAVEN_GITHUB_ORG
+
+# COPY ./jpo-conflictmonitor/pom.xml ./jpo-conflictmonitor/
+# COPY ./settings.xml ./jpo-conflictmonitor/
+
+# # Copy and Build Conflict Monitor
+# # Download dependencies alone to cache them first
+# WORKDIR /home/jpo-conflictmonitor
+# RUN mvn -s settings.xml dependency:resolve
+
+# # Copy the source code and build the conflict monitor
+# COPY ./jpo-conflictmonitor/src ./src
+# RUN mvn -s settings.xml install -DskipTests -Ppackage-jar
+
+# Copy and Build Deduplicator
+WORKDIR /home
+COPY ./jpo-deduplicator/pom.xml ./jpo-deduplicator/
+COPY ./jpo-deduplicator/settings.xml ./jpo-deduplicator/
+
+WORKDIR /home/jpo-deduplicator
+RUN mvn -s settings.xml dependency:resolve
+
+COPY ./jpo-deduplicator/src ./src
+RUN mvn -s settings.xml install -DskipTests
+
+FROM amazoncorretto:21
+
+WORKDIR /home
+
+COPY --from=builder /home/jpo-deduplicator/src/main/resources/application.yaml /home
+COPY --from=builder /home/jpo-deduplicator/src/main/resources/logback.xml /home
+COPY --from=builder /home/jpo-deduplicator/target/jpo-deduplicator.jar /home
+
+#COPY cert.crt /home/cert.crt
+#RUN keytool -import -trustcacerts -keystore /usr/local/openjdk-11/lib/security/cacerts -storepass changeit -noprompt -alias mycert -file cert.crt
+
+ENTRYPOINT ["java", \
+ "-Djava.rmi.server.hostname=$DOCKER_HOST_IP", \
+ "-Dcom.sun.management.jmxremote.port=9090", \
+ "-Dcom.sun.management.jmxremote.rmi.port=9090", \
+ "-Dcom.sun.management.jmxremote", \
+ "-Dcom.sun.management.jmxremote.local.only=true", \
+ "-Dcom.sun.management.jmxremote.authenticate=false", \
+ "-Dcom.sun.management.jmxremote.ssl=false", \
+ "-Dlogback.configurationFile=/home/logback.xml", \
+ "-jar", \
+ "/home/jpo-deduplicator.jar"]
+
+# ENTRYPOINT ["tail", "-f", "/dev/null"]
diff --git a/jpo-deduplicator/jpo-deduplicator/LICENSE-2.0.html b/jpo-deduplicator/jpo-deduplicator/LICENSE-2.0.html
new file mode 100644
index 0000000..d1055c8
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/LICENSE-2.0.html
@@ -0,0 +1,228 @@
+
+
+
+
+
+ Apache License, Version 2.0
+
+
+
+
+
Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND
+ DISTRIBUTION
+
1.
+ Definitions .
+
"License" shall mean the terms and conditions for use,
+ reproduction, and distribution as defined by Sections 1 through 9
+ of this document.
+
"Licensor" shall mean the copyright owner or entity authorized
+ by the copyright owner that is granting the License.
+
"Legal Entity" shall mean the union of the acting entity and
+ all other entities that control, are controlled by, or are under
+ common control with that entity. For the purposes of this
+ definition, "control" means (i) the power, direct or indirect, to
+ cause the direction or management of such entity, whether by
+ contract or otherwise, or (ii) ownership of fifty percent (50%)
+ or more of the outstanding shares, or (iii) beneficial ownership
+ of such entity.
+
"You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
"Source" form shall mean the preferred form for making
+ modifications, including but not limited to software source code,
+ documentation source, and configuration files.
+
"Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but not
+ limited to compiled object code, generated documentation, and
+ conversions to other media types.
+
"Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work (an
+ example is provided in the Appendix below).
+
"Derivative Works" shall mean any work, whether in Source or
+ Object form, that is based on (or derived from) the Work and for
+ which the editorial revisions, annotations, elaborations, or
+ other modifications represent, as a whole, an original work of
+ authorship. For the purposes of this License, Derivative Works
+ shall not include works that remain separable from, or merely
+ link (or bind by name) to the interfaces of, the Work and
+ Derivative Works thereof.
+
"Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or
+ additions to that Work or Derivative Works thereof, that is
+ intentionally submitted to Licensor for inclusion in the Work by
+ the copyright owner or by an individual or Legal Entity
+ authorized to submit on behalf of the copyright owner. For the
+ purposes of this definition, "submitted" means any form of
+ electronic, verbal, or written communication sent to the Licensor
+ or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control
+ systems, and issue tracking systems that are managed by, or on
+ behalf of, the Licensor for the purpose of discussing and
+ improving the Work, but excluding communication that is
+ conspicuously marked or otherwise designated in writing by the
+ copyright owner as "Not a Contribution."
+
"Contributor" shall mean Licensor and any individual or Legal
+ Entity on behalf of whom a Contribution has been received by
+ Licensor and subsequently incorporated within the Work.
+
2. Grant of
+ Copyright License . Subject to the terms and
+ conditions of this License, each Contributor hereby grants to You
+ a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+ irrevocable copyright license to reproduce, prepare Derivative
+ Works of, publicly display, publicly perform, sublicense, and
+ distribute the Work and such Derivative Works in Source or Object
+ form.
+
3. Grant of Patent
+ License . Subject to the terms and conditions of this
+ License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have
+ made, use, offer to sell, sell, import, and otherwise transfer
+ the Work, where such license applies only to those patent claims
+ licensable by such Contributor that are necessarily infringed by
+ their Contribution(s) alone or by combination of their
+ Contribution(s) with the Work to which such Contribution(s) was
+ submitted. If You institute patent litigation against any entity
+ (including a cross-claim or counterclaim in a lawsuit) alleging
+ that the Work or a Contribution incorporated within the Work
+ constitutes direct or contributory patent infringement, then any
+ patent licenses granted to You under this License for that Work
+ shall terminate as of the date such litigation is filed.
+
4.
+ Redistribution . You may reproduce and distribute
+ copies of the Work or Derivative Works thereof in any medium,
+ with or without modifications, and in Source or Object form,
+ provided that You meet the following conditions:
+
+ You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+ You must cause any modified files to carry prominent
+ notices stating that You changed the files; and
+ You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work, excluding
+ those notices that do not pertain to any part of the Derivative
+ Works; and
+ If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute
+ must include a readable copy of the attribution notices
+ contained within such NOTICE file, excluding those notices that
+ do not pertain to any part of the Derivative Works, in at least
+ one of the following places: within a NOTICE text file
+ distributed as part of the Derivative Works; within the Source
+ form or documentation, if provided along with the Derivative
+ Works; or, within a display generated by the Derivative Works,
+ if and wherever such third-party notices normally appear. The
+ contents of the NOTICE file are for informational purposes only
+ and do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed as
+ modifying the License.
+
+ You may add Your own copyright statement to Your modifications
+ and may provide additional or different license terms and
+ conditions for use, reproduction, or distribution of Your
+ modifications, or for any such Derivative Works as a whole,
+ provided Your use, reproduction, and distribution of the Work
+ otherwise complies with the conditions stated in this
+ License.
+
+
5.
+ Submission of Contributions . Unless You explicitly
+ state otherwise, any Contribution intentionally submitted for
+ inclusion in the Work by You to the Licensor shall be under the
+ terms and conditions of this License, without any additional
+ terms or conditions. Notwithstanding the above, nothing herein
+ shall supersede or modify the terms of any separate license
+ agreement you may have executed with Licensor regarding such
+ Contributions.
+
6.
+ Trademarks . This License does not grant permission
+ to use the trade names, trademarks, service marks, or product
+ names of the Licensor, except as required for reasonable and
+ customary use in describing the origin of the Work and
+ reproducing the content of the NOTICE file.
+
7. Disclaimer
+ of Warranty . Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or
+ conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or
+ FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for
+ determining the appropriateness of using or redistributing the
+ Work and assume any risks associated with Your exercise of
+ permissions under this License.
+
8. Limitation
+ of Liability . In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect,
+ special, incidental, or consequential damages of any character
+ arising as a result of this License or out of the use or
+ inability to use the Work (including but not limited to damages
+ for loss of goodwill, work stoppage, computer failure or
+ malfunction, or any and all other commercial damages or losses),
+ even if such Contributor has been advised of the possibility of
+ such damages.
+
9. Accepting
+ Warranty or Additional Liability . While
+ redistributing the Work or Derivative Works thereof, You may
+ choose to offer, and charge a fee for, acceptance of support,
+ warranty, indemnity, or other liability obligations and/or rights
+ consistent with this License. However, in accepting such
+ obligations, You may act only on Your own behalf and on Your sole
+ responsibility, not on behalf of any other Contributor, and only
+ if You agree to indemnify, defend, and hold each Contributor
+ harmless for any liability incurred by, or claims asserted
+ against, such Contributor by reason of your accepting any such
+ warranty or additional liability.
+
END OF TERMS AND CONDITIONS
+
APPENDIX: How to apply the Apache License to your
+ work
+
To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a file
+ or class name and description of purpose be included on the same
+ "printed page" as the copyright notice for easier identification
+ within third-party archives.
+
+
Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+
+
+
+
+
diff --git a/jpo-deduplicator/jpo-deduplicator/pom.xml b/jpo-deduplicator/jpo-deduplicator/pom.xml
new file mode 100644
index 0000000..3a358e2
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/pom.xml
@@ -0,0 +1,294 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.1.3
+
+
+ usdot.jpo.ode
+ jpo-deduplicator
+ 1.3.1
+ jar
+ jpo-deduplicator
+ http://maven.apache.org
+
+ UTF-8
+ 29-SNAPSHOT
+ usdot-jpo-ode
+ https://sonarcloud.io
+
+ Deduplicates Messages generated by Subcomponents of the ODE
+ 21
+
+ ^
+
+
+ 0.8.11
+ jacoco
+ reuseReports
+ ${project.basedir}/target/site/jacoco/jacoco.xml
+ java
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.apache.logging.log4j
+ log4j-to-slf4j
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ com.vaadin.external.google
+ android-json
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ runtime
+
+
+ org.springframework.boot
+ spring-boot-starter-data-rest
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.restdocs
+ spring-restdocs-mockmvc
+ test
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-websocket
+
+
+ org.springframework.kafka
+ spring-kafka
+
+
+ org.springframework.kafka
+ spring-kafka-test
+ test
+
+
+
+
+ usdot.jpo.ode
+ jpo-ode-core
+ 3.0.0
+
+
+ usdot.jpo.ode
+ jpo-ode-plugins
+ 3.0.0
+
+
+ usdot.jpo.ode
+ jpo-geojsonconverter
+ 1.4.2
+
+
+ usdot.jpo.ode
+ jpo-conflictmonitor
+ 1.5.0
+
+
+ junit
+ junit
+ 4.13.2
+ test
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ ${jacoco.version}
+ test
+
+
+ org.mockito
+ mockito-core
+ 5.10.0
+ test
+
+
+
+ com.google.guava
+ guava
+ 33.0.0-jre
+
+
+
+
+
+
+
+ org.projectlombok
+ lombok
+ 1.18.30
+
+
+
+
+
+
+
+
+ false
+
+ central
+ Central Repository
+ https://repo.maven.apache.org/maven2
+
+
+
+
+
+ ${project.artifactId}
+
+
+ org.sonarsource.scanner.maven
+ sonar-maven-plugin
+ 3.10.0.2594
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ @{argLine}
+
+
+
+ build-info
+
+ build-info
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.2.5
+
+ all
+ exit
+
+
+
+ @{argLine}
+ -XX:+EnableDynamicAgentLoading
+
+
+
+
+
+ org.apache.maven.surefire
+ surefire-junit47
+ 3.2.5
+
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ ${jacoco.version}
+
+
+ jacoco-initialize
+
+ prepare-agent
+
+
+
+ jacoco-site
+ package
+
+ report
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.3.0
+
+
+
+ jar
+
+ package
+
+
+ jpo-deduplicator
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-report-plugin
+ 3.2.5
+
+
+
+
diff --git a/jpo-deduplicator/jpo-deduplicator/settings.xml b/jpo-deduplicator/jpo-deduplicator/settings.xml
new file mode 100644
index 0000000..eaee228
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/settings.xml
@@ -0,0 +1,54 @@
+
+
+
+ default
+
+
+
+ github
+ jpo_deduplicator
+ ${env.MAVEN_GITHUB_TOKEN}
+
+
+ github_jpo_ode
+ jpo_deduplicator
+ ${env.MAVEN_GITHUB_TOKEN}
+
+
+ github_jpo_geojsonconverter
+ jpo_deduplicator
+ ${env.MAVEN_GITHUB_TOKEN}
+
+
+
+
+ default
+
+
+ github
+ GitHub JPO Conflict Monitor
+ https://maven.pkg.github.com/${env.MAVEN_GITHUB_ORG}/jpo-conflictmonitor
+
+ false
+
+
+
+ github_jpo_ode
+ GitHub JPO ODE
+ https://maven.pkg.github.com/${env.MAVEN_GITHUB_ORG}/jpo-ode
+
+ false
+
+
+
+ github_jpo_geojsonconverter
+ GitHub JPO GeojsonConverter
+ https://maven.pkg.github.com/${env.MAVEN_GITHUB_ORG}/jpo-geojsonconverter
+
+ false
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/DeduplicatorApplication.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/DeduplicatorApplication.java
new file mode 100644
index 0000000..6b483c9
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/DeduplicatorApplication.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright 2018 572682
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ ******************************************************************************/
+package us.dot.its.jpo.deduplicator;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.MBeanRegistrationException;
+import javax.management.MalformedObjectNameException;
+import javax.management.NotCompliantMBeanException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+
+@SpringBootApplication
+@EnableConfigurationProperties(DeduplicatorProperties.class)
+public class DeduplicatorApplication {
+
+ private static final Logger logger = LoggerFactory.getLogger(DeduplicatorApplication.class);
+
+ static final int DEFAULT_NO_THREADS = 10;
+ static final String DEFAULT_SCHEMA = "default";
+
+ public static void main(String[] args) throws MalformedObjectNameException, InterruptedException,
+ InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException {
+
+ logger.info("Starting Message Deduplicator");
+ SpringApplication.run(DeduplicatorApplication.class, args);
+ // MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+ // SystemConfig mBean = new SystemConfig(DEFAULT_NO_THREADS, DEFAULT_SCHEMA);
+ // ObjectName name = new ObjectName("us.dot.its.jpo.geojson:type=SystemConfig");
+ // mbs.registerMBean(mBean, name);
+ logger.info("Message Deduplicator has started");
+
+ }
+
+ @Bean
+ CommandLineRunner init(DeduplicatorProperties geojsonProperties) {
+ return args -> {
+ };
+ }
+}
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/DeduplicatorProperties.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/DeduplicatorProperties.java
new file mode 100644
index 0000000..c60f522
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/DeduplicatorProperties.java
@@ -0,0 +1,444 @@
+/*******************************************************************************
+ * Copyright 2018 572682
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ ******************************************************************************/
+package us.dot.its.jpo.deduplicator;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Properties;
+import java.util.UUID;
+
+import jakarta.annotation.PostConstruct;
+
+import org.apache.commons.lang3.SystemUtils;
+import org.apache.kafka.clients.producer.ProducerConfig;
+import org.apache.kafka.common.serialization.Serdes;
+import org.apache.kafka.streams.StreamsConfig;
+import org.apache.kafka.streams.errors.LogAndContinueExceptionHandler;
+import org.apache.kafka.streams.processor.LogAndSkipOnInvalidTimestamp;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.info.BuildProperties;
+import org.springframework.context.EnvironmentAware;
+import org.springframework.core.env.Environment;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.AccessLevel;
+import us.dot.its.jpo.conflictmonitor.AlwaysContinueProductionExceptionHandler;
+import us.dot.its.jpo.ode.eventlog.EventLogger;
+import us.dot.its.jpo.ode.util.CommonUtils;
+
+// import us.dot.its.jpo.conflictmonitor.AlwaysContinueProductionExceptionHandler;
+
+@Getter
+@Setter
+@ConfigurationProperties
+public class DeduplicatorProperties implements EnvironmentAware {
+
+ private static final Logger logger = LoggerFactory.getLogger(DeduplicatorProperties.class);
+
+ // Processed Map Configuration
+ private String kafkaTopicProcessedMap;
+ private String kafkaTopicDeduplicatedProcessedMap;
+ private boolean enableProcessedMapDeduplication;
+ private String kafkaStateStoreProcessedMapName = "ProcessedMap-store";
+
+ // Processed Map WKT Configuration
+ private String kafkaTopicProcessedMapWKT;
+ private String kafkaTopicDeduplicatedProcessedMapWKT;
+ private boolean enableProcessedMapWktDeduplication;
+ private String kafkaStateStoreProcessedMapWKTName = "ProcessedMapWKT-store";
+
+ // Ode Map Json Configuration
+ private String kafkaTopicOdeMapJson;
+ private String kafkaTopicDeduplicatedOdeMapJson;
+ private boolean enableOdeMapDeduplication;
+ private String kafkaStateStoreOdeMapJsonName = "OdeMapJson-store";
+
+ // Ode Tim Json Configuration
+ private String kafkaTopicOdeTimJson;
+ private String kafkaTopicDeduplicatedOdeTimJson;
+ private boolean enableOdeTimDeduplication;
+ private String kafkaStateStoreOdeTimJsonName = "OdeTimJson-store";
+
+ // Ode Raw Encoded Tim Json Configuration
+ private String kafkaTopicOdeRawEncodedTimJson;
+ private String kafkaTopicDeduplicatedOdeRawEncodedTimJson;
+ private boolean enableOdeRawEncodedTimDeduplication;
+ private String kafkaStateStoreOdeRawEncodedTimJsonName = "OdeRawEncodedTimJson-store";
+
+ //Ode BsmJson Configuration
+ private String kafkaTopicOdeBsmJson;
+ private String kafkaTopicDeduplicatedOdeBsmJson;
+ private boolean enableOdeBsmDeduplication;
+ private long odeBsmMaximumTimeDelta;
+ private double odeBsmMaximumPositionDelta;
+ private double odeBsmAlwaysIncludeAtSpeed;
+ private String kafkaStateStoreOdeBsmJsonName = "OdeBsmJson-store";
+
+ // Confluent Properties
+ private boolean confluentCloudEnabled = false;
+ private String confluentKey = null;
+ private String confluentSecret = null;
+
+ // Processed SPaT Configuration
+ private String kafkaTopicProcessedSpat;
+ private String kafkaTopicDeduplicatedProcessedSpat;
+ private boolean enableProcessedSpatDeduplication;
+ private String kafkaStateStoreProcessedSpatName = "ProcessedSpat-store";
+
+
+ private int lingerMs = 0;
+
+
+
+ @Autowired
+ @Setter(AccessLevel.NONE)
+ private Environment env;
+
+ /*
+ * General Properties
+ */
+ private String version;
+ // public static final int OUTPUT_SCHEMA_VERSION = 6;
+
+ @Setter(AccessLevel.NONE)
+ private String kafkaBrokers = null;
+
+ private static final String DEFAULT_KAFKA_PORT = "9092";
+ private static final String DEFAULT_CONNECT_PORT = "8083";
+
+ @Setter(AccessLevel.NONE)
+ private String hostId;
+
+ // @Setter(AccessLevel.NONE)
+ // private String connectURL = null;
+
+ // @Setter(AccessLevel.NONE)
+ // private String dockerHostIP = null;
+
+ @Setter(AccessLevel.NONE)
+ private String kafkaBrokerIP = null;
+
+
+ // @Setter(AccessLevel.NONE)
+ // private String dbHostIP = null;
+
+
+ @Setter(AccessLevel.NONE)
+ @Autowired
+ BuildProperties buildProperties;
+
+ @PostConstruct
+ void initialize() {
+ setVersion(buildProperties.getVersion());
+ logger.info("groupId: {}", buildProperties.getGroup());
+ logger.info("artifactId: {}", buildProperties.getArtifact());
+ logger.info("version: {}", version);
+ //OdeMsgMetadata.setStaticSchemaVersion(OUTPUT_SCHEMA_VERSION);
+
+
+
+ String hostname;
+ try {
+ hostname = InetAddress.getLocalHost().getHostName();
+ } catch (UnknownHostException e) {
+ // Let's just use a random hostname
+ hostname = UUID.randomUUID().toString();
+ logger.info("Unknown host error: {}, using random", e);
+ }
+ hostId = hostname;
+ logger.info("Host ID: {}", hostId);
+ EventLogger.logger.info("Initializing services on host {}", hostId);
+
+ // if(dbHostIP == null){
+ // String dbHost = CommonUtils.getEnvironmentVariable("MONGO_IP");
+
+ // if(dbHost == null){
+ // logger.warn(
+ // "DB Host IP not defined, Defaulting to localhost.");
+ // dbHost = "localhost";
+ // }
+ // dbHostIP = dbHost;
+ // }
+
+ if (kafkaBrokers == null) {
+
+ String kafkaBrokers = CommonUtils.getEnvironmentVariable("KAFKA_BOOTSTRAP_SERVERS");
+
+ logger.info("ode.kafkaBrokers property not defined. Will try KAFKA_BOOTSTRAP_SERVERS => {}", kafkaBrokers);
+
+ if (kafkaBrokers == null) {
+ logger.warn(
+ "Neither ode.kafkaBrokers ode property nor KAFKA_BOOTSTRAP_SERVERS environment variable are defined. Defaulting to localhost:9092");
+ kafkaBrokers = "localhost:9092";
+ }
+
+ }
+
+ String kafkaType = CommonUtils.getEnvironmentVariable("KAFKA_TYPE");
+ if (kafkaType != null) {
+ confluentCloudEnabled = kafkaType.equals("CONFLUENT");
+ if (confluentCloudEnabled) {
+
+ System.out.println("Enabling Confluent Cloud Integration");
+
+ confluentKey = CommonUtils.getEnvironmentVariable("CONFLUENT_KEY");
+ confluentSecret = CommonUtils.getEnvironmentVariable("CONFLUENT_SECRET");
+ }
+ }
+
+ // Initialize the Kafka Connect URL
+ // if (connectURL == null) {
+ // String connectURL = CommonUtils.getEnvironmentVariable("CONNECT_URL");
+ // if (connectURL == null) {
+ // connectURL = String.format("http://%s:%s", "localhost", DEFAULT_CONNECT_PORT);
+ // }
+ // }
+
+ // List asList = Arrays.asList(this.getKafkaTopicsDisabled());
+ // logger.info("Disabled Topics: {}", asList);
+ // kafkaTopicsDisabledSet.addAll(asList);
+ }
+
+ public Properties createStreamProperties(String name) {
+ Properties streamProps = new Properties();
+ streamProps.put(StreamsConfig.APPLICATION_ID_CONFIG, name);
+
+ streamProps.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaBrokers);
+
+ streamProps.put(StreamsConfig.DEFAULT_DESERIALIZATION_EXCEPTION_HANDLER_CLASS_CONFIG,
+ LogAndContinueExceptionHandler.class.getName());
+
+ streamProps.put(StreamsConfig.DEFAULT_TIMESTAMP_EXTRACTOR_CLASS_CONFIG,
+ LogAndSkipOnInvalidTimestamp.class.getName());
+
+ streamProps.put(StreamsConfig.DEFAULT_PRODUCTION_EXCEPTION_HANDLER_CLASS_CONFIG,
+ AlwaysContinueProductionExceptionHandler.class.getName());
+
+ streamProps.put(StreamsConfig.NUM_STREAM_THREADS_CONFIG, 2);
+
+ // streamProps.put(StreamsConfig.producerPrefix("acks"), "all");
+ streamProps.put(StreamsConfig.producerPrefix(ProducerConfig.ACKS_CONFIG), "all");
+
+ // Reduce cache buffering per topology to 1MB
+ streamProps.put(StreamsConfig.STATESTORE_CACHE_MAX_BYTES_CONFIG, 1 * 1024 * 1024L);
+
+ // Decrease default commit interval. Default for 'at least once' mode of 30000ms
+ // is too slow.
+ streamProps.put(StreamsConfig.COMMIT_INTERVAL_MS_CONFIG, 100);
+
+ // All the keys are Strings in this app
+ streamProps.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass().getName());
+
+ // Configure the state store location
+ if (SystemUtils.IS_OS_LINUX) {
+ streamProps.put(StreamsConfig.STATE_DIR_CONFIG, "/var/lib/ode/kafka-streams");
+ } else if (SystemUtils.IS_OS_WINDOWS) {
+ streamProps.put(StreamsConfig.STATE_DIR_CONFIG, "C:/temp/ode");
+ }
+ // streamProps.put(StreamsConfig.STATE_DIR_CONFIG, "/var/lib/")\
+
+ // Increase max.block.ms and delivery.timeout.ms for streams
+ final int FIVE_MINUTES_MS = 5 * 60 * 1000;
+ streamProps.put(ProducerConfig.MAX_BLOCK_MS_CONFIG, FIVE_MINUTES_MS);
+ streamProps.put(ProducerConfig.DELIVERY_TIMEOUT_MS_CONFIG, FIVE_MINUTES_MS);
+
+ // Disable batching
+ // streamProps.put(ProducerConfig.BATCH_SIZE_CONFIG, 0);
+
+ streamProps.put(ProducerConfig.COMPRESSION_TYPE_CONFIG, "zstd");
+ streamProps.put(ProducerConfig.LINGER_MS_CONFIG, getKafkaLingerMs());
+
+ if (confluentCloudEnabled) {
+ streamProps.put("ssl.endpoint.identification.algorithm", "https");
+ streamProps.put("security.protocol", "SASL_SSL");
+ streamProps.put("sasl.mechanism", "PLAIN");
+
+ if (confluentKey != null && confluentSecret != null) {
+ String auth = "org.apache.kafka.common.security.plain.PlainLoginModule required " +
+ "username=\"" + confluentKey + "\" " +
+ "password=\"" + confluentSecret + "\";";
+ streamProps.put("sasl.jaas.config", auth);
+ }
+ else {
+ logger.error("Environment variables CONFLUENT_KEY and CONFLUENT_SECRET are not set. Set these in the .env file to use Confluent Cloud");
+ }
+ }
+
+
+ return streamProps;
+ }
+
+ public String getProperty(String key) {
+ return env.getProperty(key);
+ }
+
+ public String getProperty(String key, String defaultValue) {
+ return env.getProperty(key, defaultValue);
+ }
+
+ public Object getProperty(String key, int i) {
+ return env.getProperty(key, Integer.class, i);
+ }
+
+ public Boolean getConfluentCloudStatus() {
+ return confluentCloudEnabled;
+ }
+
+
+ @Value("${kafkaTopicProcessedMap}")
+ public void setKafkaTopicProcessedMap(String kafkaTopicProcessedMap) {
+ this.kafkaTopicProcessedMap = kafkaTopicProcessedMap;
+ }
+
+ @Value("${kafkaTopicDeduplicatedProcessedMap}")
+ public void setKafkaTopicDeduplicatedProcessedMap(String kafkaTopicDeduplicatedProcessedMap) {
+ this.kafkaTopicDeduplicatedProcessedMap = kafkaTopicDeduplicatedProcessedMap;
+ }
+
+ @Value("${enableProcessedMapDeduplication}")
+ public void setEnableProcessedMapDeduplication(boolean enableProcessedMapDeduplication) {
+ this.enableProcessedMapDeduplication = enableProcessedMapDeduplication;
+ }
+
+ @Value("${kafkaTopicProcessedMapWKT}")
+ public void setKafkaTopicProcessedMapWKT(String kafkaTopicProcessedMapWKT) {
+ this.kafkaTopicProcessedMapWKT = kafkaTopicProcessedMapWKT;
+ }
+
+ @Value("${kafkaTopicDeduplicatedProcessedMapWKT}")
+ public void setKafkaTopicDeduplicatedProcessedMapWKT(String kafkaTopicDeduplicatedProcessedMapWKT) {
+ this.kafkaTopicDeduplicatedProcessedMapWKT = kafkaTopicDeduplicatedProcessedMapWKT;
+ }
+
+ @Value("${enableProcessedMapWktDeduplication}")
+ public void setEnableProcessedMapWktDeduplication(boolean enableProcessedMapWktDeduplication) {
+ this.enableProcessedMapWktDeduplication = enableProcessedMapWktDeduplication;
+ }
+
+ @Value("${kafkaTopicOdeMapJson}")
+ public void setKafkaTopicOdeMapJson(String kafkaTopicOdeMapJson) {
+ this.kafkaTopicOdeMapJson = kafkaTopicOdeMapJson;
+ }
+
+ @Value("${kafkaTopicDeduplicatedOdeMapJson}")
+ public void setKafkaTopicDeduplicatedOdeMapJson(String kafkaTopicDeduplicatedOdeMapJson) {
+ this.kafkaTopicDeduplicatedOdeMapJson = kafkaTopicDeduplicatedOdeMapJson;
+ }
+
+ @Value("${enableOdeMapDeduplication}")
+ public void setEnableOdeMapDeduplication(boolean enableOdeMapDeduplication) {
+ this.enableOdeMapDeduplication = enableOdeMapDeduplication;
+ }
+
+ @Value("${kafkaTopicOdeTimJson}")
+ public void setKafkaTopicOdeTimJson(String kafkaTopicOdeTimJson) {
+ this.kafkaTopicOdeTimJson = kafkaTopicOdeTimJson;
+ }
+
+ @Value("${kafkaTopicDeduplicatedOdeTimJson}")
+ public void setKafkaTopicDeduplicatedOdeTimJson(String kafkaTopicDeduplicatedOdeTimJson) {
+ this.kafkaTopicDeduplicatedOdeTimJson = kafkaTopicDeduplicatedOdeTimJson;
+ }
+
+ @Value("${enableOdeTimDeduplication}")
+ public void setEnableOdeTimDeduplication(boolean enableOdeTimDeduplication) {
+ this.enableOdeTimDeduplication = enableOdeTimDeduplication;
+ }
+
+ @Value("${kafkaTopicOdeRawEncodedTimJson}")
+ public void setKafkaTopicOdeRawEncodedTimJson(String kafkaTopicOdeRawEncodedTimJson) {
+ this.kafkaTopicOdeRawEncodedTimJson = kafkaTopicOdeRawEncodedTimJson;
+ }
+
+ @Value("${kafkaTopicDeduplicatedOdeRawEncodedTimJson}")
+ public void setKafkaTopicDeduplicatedOdeRawEncodedTimJson(String kafkaTopicDeduplicatedOdeRawEncodedTimJson) {
+ this.kafkaTopicDeduplicatedOdeRawEncodedTimJson = kafkaTopicDeduplicatedOdeRawEncodedTimJson;
+ }
+
+ @Value("${enableOdeRawEncodedTimDeduplication}")
+ public void setEnableOdeRawEncodedTimDeduplication(boolean enableOdeRawEncodedTimDeduplication) {
+ this.enableOdeRawEncodedTimDeduplication = enableOdeRawEncodedTimDeduplication;
+ }
+
+ @Value("${kafkaTopicOdeBsmJson}")
+ public void setkafkaTopicOdeBsmJson(String kafkaTopicOdeBsmJson) {
+ this.kafkaTopicOdeBsmJson = kafkaTopicOdeBsmJson;
+ }
+
+ @Value("${kafkaTopicDeduplicatedOdeBsmJson}")
+ public void setkafkaTopicDeduplicatedOdeBsmJson(String kafkaTopicDeduplicatedOdeBsmJson) {
+ this.kafkaTopicDeduplicatedOdeBsmJson = kafkaTopicDeduplicatedOdeBsmJson;
+ }
+
+ @Value("${enableOdeBsmDeduplication}")
+ public void setenableOdeBsmDeduplication(boolean enableOdeBsmDeduplication) {
+ this.enableOdeBsmDeduplication = enableOdeBsmDeduplication;
+ }
+
+ @Value("${odeBsmMaximumTimeDelta}")
+ public void setMaximumTimeDelta(long maximumTimeDelta) {
+ this.odeBsmMaximumTimeDelta = maximumTimeDelta;
+ }
+
+ @Value("${odeBsmMaximumPositionDelta}")
+ public void setMaximumPositionDelta(double maximumPositionDelta) {
+ this.odeBsmMaximumPositionDelta = maximumPositionDelta;
+ }
+
+ @Value("${odeBsmAlwaysIncludeAtSpeed}")
+ public void setAlwaysIncludeAtSpeed(double alwaysIncludeAtSpeed) {
+ this.odeBsmAlwaysIncludeAtSpeed = alwaysIncludeAtSpeed;
+ }
+
+ @Value("${spring.kafka.bootstrap-servers}")
+ public void setKafkaBrokers(String kafkaBrokers) {
+ this.kafkaBrokers = kafkaBrokers;
+ }
+
+ @Value("${kafkaTopicProcessedSpat}")
+ public void setKafkaTopicProcessedSpat(String kafkaTopicProcessedSpat) {
+ this.kafkaTopicProcessedSpat = kafkaTopicProcessedSpat;
+ }
+
+ @Value("${kafkaTopicDeduplicatedProcessedSpat}")
+ public void setKafkaTopicDeduplicatedProcessedSpat(String kafkaTopicDeduplicatedProcessedSpat) {
+ this.kafkaTopicDeduplicatedProcessedSpat = kafkaTopicDeduplicatedProcessedSpat;
+ }
+
+ @Value("${enableProcessedSpatDeduplication}")
+ public void setEnableProcessedSpatDeduplication(boolean enableProcessedSpatDeduplication) {
+ this.enableProcessedSpatDeduplication = enableProcessedSpatDeduplication;
+ }
+
+ @Override
+ public void setEnvironment(Environment environment) {
+ env = environment;
+ }
+
+ @Value("${kafka.linger_ms}")
+ public void setKafkaLingerMs(int lingerMs) {
+ this.lingerMs = lingerMs;
+ }
+
+ public int getKafkaLingerMs() {
+ return lingerMs;
+ }
+}
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/KafkaConfiguration.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/KafkaConfiguration.java
new file mode 100644
index 0000000..a5cac5f
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/KafkaConfiguration.java
@@ -0,0 +1,197 @@
+package us.dot.its.jpo.deduplicator;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import org.apache.kafka.clients.admin.NewTopic;
+import org.apache.kafka.common.serialization.StringSerializer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.kafka.config.TopicBuilder;
+import org.springframework.kafka.core.DefaultKafkaProducerFactory;
+import org.springframework.kafka.core.KafkaAdmin;
+import org.springframework.kafka.core.KafkaAdmin.NewTopics;
+import org.springframework.kafka.core.KafkaTemplate;
+import org.springframework.kafka.core.ProducerFactory;
+import org.springframework.stereotype.Component;
+import org.apache.kafka.clients.producer.ProducerConfig;
+
+@Component
+@ConfigurationProperties(prefix = "kafka.topics")
+public class KafkaConfiguration {
+
+ @Autowired
+ private DeduplicatorProperties properties;
+
+ @Bean(name = "createKafkaTopics")
+ public KafkaAdmin.NewTopics createKafkaTopics() {
+ logger.info("createTopic");
+ List newTopics = new ArrayList<>();
+
+ if(!properties.getConfluentCloudStatus()){
+ try {
+ for(var propEntry : admin.getConfigurationProperties().entrySet()) {
+ logger.info("KafkaAdmin property {} = {}", propEntry.getKey(), propEntry.getValue());
+ }
+
+ if (!autoCreateTopics) {
+ logger.info("Auto create topics is disabled");
+ return null;
+ }
+
+ logger.info("Creating topics: {}", createTopics);
+
+
+
+ List topicNames = new ArrayList<>();
+ for (var topic : createTopics) {
+
+ // Get the name and config settings for the topic
+ String topicName = (String)topic.getOrDefault("name", null);
+ if (topicName == null) {
+ logger.error("CreateTopic {} has no topic name", topic);
+ break;
+ }
+ topicNames.add(topicName);
+
+ Map topicConfigs = new HashMap<>();
+ String cleanupPolicy = (String)topic.getOrDefault("cleanupPolicy", null);
+ if (cleanupPolicy != null) {
+ topicConfigs.put("cleanup.policy", cleanupPolicy);
+ }
+ Integer retentionMs = (Integer)topic.getOrDefault("retentionMs", null);
+ if (retentionMs != null) {
+ topicConfigs.put("retention.ms", retentionMs.toString());
+ }
+
+ NewTopic newTopic = TopicBuilder
+ .name(topicName)
+ .partitions(numPartitions)
+ .replicas(numReplicas)
+ .configs(topicConfigs)
+ .build();
+ newTopics.add(newTopic);
+ logger.info("New Topic: {}", newTopic);
+ }
+
+ // Explicitly create the topics here to prevent error on first run
+ admin.initialize();
+ admin.createOrModifyTopics(newTopics.toArray(new NewTopic[0]));
+
+ // Check that topics were created
+ var topicDescMap = admin.describeTopics(topicNames.toArray(new String[0]));
+ for (var entry : topicDescMap.entrySet()) {
+ String topicName = entry.getKey();
+ var desc = entry.getValue();
+ logger.info("Created topic {}: {}", topicName, desc);
+ }
+
+
+ } catch (Exception e) {
+ logger.error("Exception in createKafkaTopics", e);
+ throw e;
+ }
+ }
+
+ // List out existing topics
+ // Admin adminClient = Admin.create(properties.createStreamProperties("DeduplicatorAdminClient"));
+ // ListTopicsOptions listTopicsOptions = new ListTopicsOptions().listInternal(true);
+ // ListTopicsResult topicsResult = adminClient.listTopics(listTopicsOptions);
+ // KafkaFuture> topicsFuture = topicsResult.names();
+ // try {
+ // List topicNames = new ArrayList<>();
+ // for(String topicName: topicsFuture.get()){
+ // logger.info("Found Topic: " + topicName);
+ // topicNames.add(topicName);
+ // }
+
+ // } catch (InterruptedException e) {
+ // logger.error("Interruption Exception in createKafkaTopics. Unable to List existing Topics", e);
+ // } catch (ExecutionException e) {
+ // logger.error("Execution Exception in createKafkaTopics. Unable to List existing Topics", e);
+ // }
+
+
+ return new NewTopics(newTopics.toArray(NewTopic[]::new));
+ }
+
+ @Bean
+ public ProducerFactory producerFactory() {
+ Properties configProps = properties.createStreamProperties("Deduplicator-producer-factory");
+
+ Map map = new HashMap<>();
+
+ for (Map.Entry entry : configProps.entrySet()) {
+ String key = (String) entry.getKey();
+ Object value = entry.getValue();
+ map.put(key, value);
+ }
+
+ map.put(
+ ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,
+ StringSerializer.class);
+ map.put(
+ ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,
+ StringSerializer.class);
+
+ return new DefaultKafkaProducerFactory(map);
+ }
+
+ @Bean
+ public KafkaTemplate kafkaTemplate() {
+ return new KafkaTemplate<>(producerFactory());
+ }
+
+ private static final Logger logger = LoggerFactory.getLogger(KafkaConfiguration.class);
+
+ private boolean autoCreateTopics;
+ private int numPartitions;
+ private int numReplicas;
+ private List> createTopics;
+
+ public boolean getAutoCreateTopics() {
+ return autoCreateTopics;
+ }
+
+ public void setAutoCreateTopics(boolean autoCreateTopics) {
+ this.autoCreateTopics = autoCreateTopics;
+ }
+
+ public int getNumPartitions() {
+ return numPartitions;
+ }
+
+ public int getNumReplicas() {
+ return numReplicas;
+ }
+
+ public List> getCreateTopics() {
+ return createTopics;
+ }
+
+ public void setNumPartitions(int numPartitions) {
+ this.numPartitions = numPartitions;
+ logger.info("kafka.topics.numPartitions = {}", numPartitions);
+ }
+
+ public void setNumReplicas(int numReplicas) {
+ this.numReplicas = numReplicas;
+ }
+
+ public void setCreateTopics(List> createTopics) {
+ this.createTopics = createTopics;
+ }
+
+
+
+ @Autowired
+ private KafkaAdmin admin;
+
+
+
+}
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/StreamsExceptionHandler.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/StreamsExceptionHandler.java
new file mode 100644
index 0000000..71132f3
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/StreamsExceptionHandler.java
@@ -0,0 +1,56 @@
+package us.dot.its.jpo.deduplicator;
+
+import org.apache.kafka.streams.errors.StreamsUncaughtExceptionHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.kafka.core.KafkaTemplate;
+
+
+
+/**
+ * Handler for unhandled exceptions thrown from Streams topologies that
+ * logs the exception to a topic, and allows choosing the shutdown behavior.
+ *
+ * See {@link https://cwiki.apache.org/confluence/display/KAFKA/KIP-671%3A+Introduce+Kafka+Streams+Specific+Uncaught+Exception+Handler}
+ * for a description of the options.
+ */
+public class StreamsExceptionHandler implements StreamsUncaughtExceptionHandler {
+
+ final static Logger logger = LoggerFactory.getLogger(StreamsExceptionHandler.class);
+
+ final KafkaTemplate kafkaTemplate;
+ final String topology;
+ final String notificationTopic;
+
+ @Autowired
+ public StreamsExceptionHandler(KafkaTemplate kafkaTemplate, String topology, String notificationTopic) {
+ this.kafkaTemplate = kafkaTemplate;
+ this.topology = topology;
+ this.notificationTopic = notificationTopic;
+ }
+
+ @Override
+ public StreamThreadExceptionResponse handle(Throwable exception) {
+ try {
+ logger.error(String.format("Uncaught exception in stream topology %s", topology), exception);
+ } catch (Exception ex) {
+ logger.error("Exception sending kafka streams error event", ex);
+ }
+
+
+ // SHUTDOWN_CLIENT option shuts down quickly.
+ return StreamThreadExceptionResponse.SHUTDOWN_CLIENT;
+
+ // SHUTDOWN_APPLICATION shuts down more slowly, but cleans up more thoroughly
+ //return StreamThreadExceptionResponse.SHUTDOWN_APPLICATION;
+
+ // "Replace Thread" mode can be used to keep the streams client alive,
+ // however if the cause of the error was not transient, but due to a code error processing
+ // a record, it can result in the record being repeatedly processed throwing the
+ // same error
+ //
+ //return StreamThreadExceptionResponse.REPLACE_THREAD;
+ }
+
+}
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/DeduplicatorServiceController.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/DeduplicatorServiceController.java
new file mode 100644
index 0000000..89b44da
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/DeduplicatorServiceController.java
@@ -0,0 +1,107 @@
+package us.dot.its.jpo.deduplicator.deduplicator;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.kafka.streams.KafkaStreams;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.DependsOn;
+import org.springframework.context.annotation.Profile;
+import org.springframework.kafka.core.KafkaTemplate;
+import org.springframework.stereotype.Controller;
+
+import lombok.Getter;
+import us.dot.its.jpo.conflictmonitor.monitor.MonitorServiceController;
+import us.dot.its.jpo.conflictmonitor.monitor.algorithms.StreamsTopology;
+import us.dot.its.jpo.deduplicator.DeduplicatorProperties;
+import us.dot.its.jpo.deduplicator.deduplicator.topologies.BsmDeduplicatorTopology;
+import us.dot.its.jpo.deduplicator.deduplicator.topologies.MapDeduplicatorTopology;
+import us.dot.its.jpo.deduplicator.deduplicator.topologies.TimDeduplicatorTopology;
+import us.dot.its.jpo.deduplicator.deduplicator.topologies.OdeRawEncodedTimDeduplicatorTopology;
+import us.dot.its.jpo.deduplicator.deduplicator.topologies.ProcessedMapDeduplicatorTopology;
+import us.dot.its.jpo.deduplicator.deduplicator.topologies.ProcessedMapWktDeduplicatorTopology;
+import us.dot.its.jpo.deduplicator.deduplicator.topologies.ProcessedSpatDeduplicatorTopology;
+
+@Controller
+@DependsOn("createKafkaTopics")
+@Profile("!test && !testConfig")
+public class DeduplicatorServiceController {
+
+ private static final Logger logger = LoggerFactory.getLogger(MonitorServiceController.class);
+
+ // Temporary for KafkaStreams that don't implement the Algorithm interface
+ @Getter
+ final ConcurrentHashMap streamsMap = new ConcurrentHashMap();
+
+ @Getter
+ final ConcurrentHashMap algoMap = new ConcurrentHashMap();
+
+
+
+ @Autowired
+ public DeduplicatorServiceController(final DeduplicatorProperties props,
+ final KafkaTemplate kafkaTemplate) {
+
+
+ try {
+
+ if(props.isEnableProcessedMapDeduplication()){
+ ProcessedMapDeduplicatorTopology processedMapDeduplicatorTopology = new ProcessedMapDeduplicatorTopology(
+ props,
+ props.createStreamProperties("ProcessedMapDeduplicator")
+ );
+ processedMapDeduplicatorTopology.start();
+ }
+
+ if(props.isEnableProcessedMapWktDeduplication()){
+ ProcessedMapWktDeduplicatorTopology processedMapWktDeduplicatorTopology = new ProcessedMapWktDeduplicatorTopology(
+ props,
+ props.createStreamProperties("ProcessedMapWKTdeduplicator")
+ );
+ processedMapWktDeduplicatorTopology.start();
+ }
+
+ if(props.isEnableProcessedMapDeduplication()){
+ MapDeduplicatorTopology mapDeduplicatorTopology = new MapDeduplicatorTopology(
+ props,
+ props.createStreamProperties("MapDeduplicator")
+ );
+ mapDeduplicatorTopology.start();
+ }
+
+ if(props.isEnableOdeTimDeduplication()){
+ TimDeduplicatorTopology timDeduplicatorTopology = new TimDeduplicatorTopology(
+ props,
+ props.createStreamProperties("TimDeduplicator")
+ );
+ timDeduplicatorTopology.start();
+ }
+
+ if(props.isEnableOdeRawEncodedTimDeduplication()){
+ OdeRawEncodedTimDeduplicatorTopology odeRawEncodedTimDeduplicatorTopology = new OdeRawEncodedTimDeduplicatorTopology(
+ props,
+ props.createStreamProperties("OdeRawEncodedTimDeduplicator")
+ );
+ odeRawEncodedTimDeduplicatorTopology.start();
+ }
+
+ if(props.isEnableProcessedSpatDeduplication()){
+ ProcessedSpatDeduplicatorTopology processedSpatDeduplicatorTopology = new ProcessedSpatDeduplicatorTopology(
+ props,
+ props.createStreamProperties("ProcessedSpatDeduplicator")
+ );
+ processedSpatDeduplicatorTopology.start();
+ }
+
+ if(props.isEnableOdeBsmDeduplication()){
+ BsmDeduplicatorTopology bsmDeduplicatorTopology = new BsmDeduplicatorTopology(props);
+ bsmDeduplicatorTopology.start();
+ }
+
+
+ } catch (Exception e) {
+ logger.error("Encountered issue with creating topologies", e);
+ }
+ }
+}
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/models/JsonPair.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/models/JsonPair.java
new file mode 100644
index 0000000..d0f4377
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/models/JsonPair.java
@@ -0,0 +1,21 @@
+package us.dot.its.jpo.deduplicator.deduplicator.models;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@NoArgsConstructor
+@Setter
+@Getter
+public class JsonPair {
+
+ public JsonNode message;
+ public boolean shouldSend;
+
+ public JsonPair(JsonNode message, boolean shouldSend){
+ this.message = message;
+ this.shouldSend = shouldSend;
+ }
+}
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/models/OdeBsmPair.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/models/OdeBsmPair.java
new file mode 100644
index 0000000..208622b
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/models/OdeBsmPair.java
@@ -0,0 +1,20 @@
+package us.dot.its.jpo.deduplicator.deduplicator.models;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import us.dot.its.jpo.ode.model.OdeBsmData;
+
+@NoArgsConstructor
+@Setter
+@Getter
+public class OdeBsmPair {
+
+ public OdeBsmData message;
+ public boolean shouldSend;
+
+ public OdeBsmPair(OdeBsmData message, boolean shouldSend){
+ this.message = message;
+ this.shouldSend = shouldSend;
+ }
+}
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/models/OdeMapPair.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/models/OdeMapPair.java
new file mode 100644
index 0000000..24c265a
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/models/OdeMapPair.java
@@ -0,0 +1,20 @@
+package us.dot.its.jpo.deduplicator.deduplicator.models;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import us.dot.its.jpo.ode.model.OdeMapData;
+
+@NoArgsConstructor
+@Setter
+@Getter
+public class OdeMapPair {
+
+ public OdeMapData message;
+ public boolean shouldSend;
+
+ public OdeMapPair(OdeMapData message, boolean shouldSend){
+ this.message = message;
+ this.shouldSend = shouldSend;
+ }
+}
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/models/Pair.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/models/Pair.java
new file mode 100644
index 0000000..fd0ee44
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/models/Pair.java
@@ -0,0 +1,19 @@
+package us.dot.its.jpo.deduplicator.deduplicator.models;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@NoArgsConstructor
+@Setter
+@Getter
+public class Pair {
+
+ public T message;
+ public boolean shouldSend;
+
+ public Pair(T message, boolean shouldSend){
+ this.message = message;
+ this.shouldSend = shouldSend;
+ }
+}
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/models/ProcessedMapPair.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/models/ProcessedMapPair.java
new file mode 100644
index 0000000..b1400c5
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/models/ProcessedMapPair.java
@@ -0,0 +1,21 @@
+package us.dot.its.jpo.deduplicator.deduplicator.models;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import us.dot.its.jpo.geojsonconverter.pojos.geojson.LineString;
+import us.dot.its.jpo.geojsonconverter.pojos.geojson.map.ProcessedMap;
+
+@NoArgsConstructor
+@Setter
+@Getter
+public class ProcessedMapPair {
+
+ public ProcessedMap message;
+ public boolean shouldSend;
+
+ public ProcessedMapPair(ProcessedMap message, boolean shouldSend){
+ this.message = message;
+ this.shouldSend = shouldSend;
+ }
+}
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/models/ProcessedMapWktPair.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/models/ProcessedMapWktPair.java
new file mode 100644
index 0000000..d6acaca
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/models/ProcessedMapWktPair.java
@@ -0,0 +1,20 @@
+package us.dot.its.jpo.deduplicator.deduplicator.models;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import us.dot.its.jpo.geojsonconverter.pojos.geojson.map.ProcessedMap;
+
+@NoArgsConstructor
+@Setter
+@Getter
+public class ProcessedMapWktPair {
+
+ public ProcessedMap message;
+ public boolean shouldSend;
+
+ public ProcessedMapWktPair(ProcessedMap message, boolean shouldSend){
+ this.message = message;
+ this.shouldSend = shouldSend;
+ }
+}
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/DeduplicationProcessor.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/DeduplicationProcessor.java
new file mode 100644
index 0000000..55b669c
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/DeduplicationProcessor.java
@@ -0,0 +1,69 @@
+package us.dot.its.jpo.deduplicator.deduplicator.processors;
+
+import java.time.Duration;
+import java.time.Instant;
+
+import org.apache.kafka.streams.processor.PunctuationType;
+import org.apache.kafka.streams.processor.api.Processor;
+import org.apache.kafka.streams.processor.api.ProcessorContext;
+import org.apache.kafka.streams.processor.api.Record;
+import org.apache.kafka.streams.state.KeyValueStore;
+
+import org.apache.kafka.streams.state.KeyValueIterator;
+import org.apache.kafka.streams.KeyValue;
+
+public abstract class DeduplicationProcessor implements Processor{
+
+ private ProcessorContext context;
+ private KeyValueStore store;
+ public String storeName;
+
+ @Override
+ public void init(ProcessorContext context) {
+ this.context = context;
+ store = context.getStateStore(storeName);
+ this.context.schedule(Duration.ofHours(1), PunctuationType.WALL_CLOCK_TIME, this::cleanupOldKeys);
+ }
+
+ @Override
+ public void process(Record record) {
+
+ // Don't do anything if key is bad
+ if(record.key().equals("")){
+ return;
+ }
+
+ T lastRecord = store.get(record.key());
+ if(lastRecord == null){
+ store.put(record.key(), record.value());
+ context.forward(record);
+ return;
+ }
+
+ if(!isDuplicate(lastRecord, record.value())){
+ store.put(record.key(), record.value());
+ context.forward(record);
+ return;
+ }
+ }
+
+ private void cleanupOldKeys(final long timestamp) {
+ try (KeyValueIterator iterator = store.all()) {
+ while (iterator.hasNext()) {
+
+ KeyValue record = iterator.next();
+ // Delete any record more than 2 hours old.
+ if(Instant.ofEpochMilli(timestamp).minusSeconds(2 * 60 * 60).isAfter(getMessageTime(record.value))){
+ store.delete(record.key);
+ }
+ }
+ }
+ }
+
+ // returns an instant representing the time of the message
+ public abstract Instant getMessageTime(T message);
+
+ // returns if two messages are duplicates of one another
+ public abstract boolean isDuplicate(T lastMessage, T newMessage);
+
+}
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/OdeBsmJsonProcessor.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/OdeBsmJsonProcessor.java
new file mode 100644
index 0000000..2197b23
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/OdeBsmJsonProcessor.java
@@ -0,0 +1,80 @@
+package us.dot.its.jpo.deduplicator.deduplicator.processors;
+
+
+import java.time.Duration;
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
+
+import org.geotools.referencing.GeodeticCalculator;
+
+import us.dot.its.jpo.deduplicator.DeduplicatorProperties;
+import us.dot.its.jpo.ode.model.OdeBsmData;
+import us.dot.its.jpo.ode.model.OdeBsmMetadata;
+import us.dot.its.jpo.ode.plugin.j2735.J2735Bsm;
+import us.dot.its.jpo.ode.plugin.j2735.J2735BsmCoreData;
+
+public class OdeBsmJsonProcessor extends DeduplicationProcessor{
+
+ DateTimeFormatter formatter = DateTimeFormatter.ISO_INSTANT;
+ DeduplicatorProperties props;
+ GeodeticCalculator calculator;
+
+ public OdeBsmJsonProcessor(String storeName, DeduplicatorProperties props){
+ this.storeName = storeName;
+ this.props = props;
+ calculator = new GeodeticCalculator();
+ }
+
+
+ @Override
+ public Instant getMessageTime(OdeBsmData message) {
+ try {
+ String time = ((OdeBsmMetadata)message.getMetadata()).getOdeReceivedAt();
+ return Instant.from(formatter.parse(time));
+ } catch (Exception e) {
+ System.out.println("Failed to Parse Time");
+ return Instant.ofEpochMilli(0);
+ }
+ }
+
+ @Override
+ public boolean isDuplicate(OdeBsmData lastMessage, OdeBsmData newMessage) {
+ Instant newValueTime = getMessageTime(newMessage);
+ Instant oldValueTime = getMessageTime(lastMessage);
+
+ // If the messages are more than a certain time apart, forward the new message on
+ if(newValueTime.minus(Duration.ofMillis(props.getOdeBsmMaximumTimeDelta())).isAfter(oldValueTime)){
+ return false;
+ }
+
+ J2735BsmCoreData oldCore = ((J2735Bsm)lastMessage.getPayload().getData()).getCoreData();
+ J2735BsmCoreData newCore = ((J2735Bsm)newMessage.getPayload().getData()).getCoreData();
+
+
+ // If the Vehicle is moving, forward the message on
+ if(newCore.getSpeed().doubleValue() > props.getOdeBsmAlwaysIncludeAtSpeed()){
+ return false;
+ }
+
+
+ double distance = calculateGeodeticDistance(
+ newCore.getPosition().getLatitude().doubleValue(),
+ newCore.getPosition().getLongitude().doubleValue(),
+ oldCore.getPosition().getLatitude().doubleValue(),
+ oldCore.getPosition().getLongitude().doubleValue()
+ );
+
+ // If the position delta between the messages is suitable large, forward the message on
+ if(distance > props.getOdeBsmMaximumPositionDelta()){
+ return false;
+ }
+
+ return true;
+ }
+
+ public double calculateGeodeticDistance(double lat1, double lon1, double lat2, double lon2) {
+ calculator.setStartingGeographicPoint(lon1, lat1);
+ calculator.setDestinationGeographicPoint(lon2, lat2);
+ return calculator.getOrthodromicDistance();
+ }
+}
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/OdeMapJsonProcessor.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/OdeMapJsonProcessor.java
new file mode 100644
index 0000000..adaf6db
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/OdeMapJsonProcessor.java
@@ -0,0 +1,70 @@
+package us.dot.its.jpo.deduplicator.deduplicator.processors;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
+import java.util.Objects;
+
+import us.dot.its.jpo.deduplicator.DeduplicatorProperties;
+import us.dot.its.jpo.ode.model.OdeMapData;
+import us.dot.its.jpo.ode.model.OdeMapMetadata;
+import us.dot.its.jpo.ode.model.OdeMapPayload;
+
+public class OdeMapJsonProcessor extends DeduplicationProcessor{
+
+ DateTimeFormatter formatter = DateTimeFormatter.ISO_INSTANT;
+
+ DeduplicatorProperties props;
+
+ public OdeMapJsonProcessor(DeduplicatorProperties props){
+ this.props = props;
+ this.storeName = props.getKafkaStateStoreOdeMapJsonName();
+ }
+
+
+ @Override
+ public Instant getMessageTime(OdeMapData message) {
+ try {
+ String time = ((OdeMapMetadata)message.getMetadata()).getOdeReceivedAt();
+ return Instant.from(formatter.parse(time));
+ } catch (Exception e) {
+ return Instant.ofEpochMilli(0);
+ }
+ }
+
+ @Override
+ public boolean isDuplicate(OdeMapData lastMessage, OdeMapData newMessage) {
+
+ Instant newValueTime = getMessageTime(newMessage);
+ Instant oldValueTime = getMessageTime(lastMessage);
+
+ if(newValueTime.minus(Duration.ofHours(1)).isAfter(oldValueTime)){
+ return false;
+
+ }else{
+ OdeMapPayload oldPayload = (OdeMapPayload)lastMessage.getPayload();
+ OdeMapPayload newPayload = (OdeMapPayload)newMessage.getPayload();
+
+ Integer oldTimestamp = oldPayload.getMap().getTimeStamp();
+ Integer newTimestamp = newPayload.getMap().getTimeStamp();
+
+
+ newPayload.getMap().setTimeStamp(oldTimestamp);
+
+ int oldHash = hashMapMessage(lastMessage);
+ int newhash = hashMapMessage(newMessage);
+
+ if(oldHash != newhash){
+ newPayload.getMap().setTimeStamp(newTimestamp);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public int hashMapMessage(OdeMapData map){
+ OdeMapPayload payload = (OdeMapPayload)map.getPayload();
+ return Objects.hash(payload.toJson());
+
+ }
+}
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/OdeRawEncodedTimJsonProcessor.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/OdeRawEncodedTimJsonProcessor.java
new file mode 100644
index 0000000..cca49a7
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/OdeRawEncodedTimJsonProcessor.java
@@ -0,0 +1,43 @@
+package us.dot.its.jpo.deduplicator.deduplicator.processors;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import us.dot.its.jpo.deduplicator.DeduplicatorProperties;
+
+public class OdeRawEncodedTimJsonProcessor extends DeduplicationProcessor{
+
+ DateTimeFormatter formatter = DateTimeFormatter.ISO_INSTANT;
+
+ DeduplicatorProperties props;
+
+ public OdeRawEncodedTimJsonProcessor(DeduplicatorProperties props){
+ this.props = props;
+ this.storeName = props.getKafkaStateStoreOdeRawEncodedTimJsonName();
+ }
+
+
+ @Override
+ public Instant getMessageTime(JsonNode message) {
+ try {
+ String time = message.get("metadata").get("odeReceivedAt").asText();
+ return Instant.from(formatter.parse(time));
+ } catch (Exception e) {
+ return Instant.ofEpochMilli(0);
+ }
+ }
+
+ @Override
+ public boolean isDuplicate(JsonNode lastMessage, JsonNode newMessage) {
+ Instant oldValueTime = getMessageTime(lastMessage);
+ Instant newValueTime = getMessageTime(newMessage);
+
+ if(newValueTime.minus(Duration.ofHours(1)).isAfter(oldValueTime)){
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/OdeTimJsonProcessor.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/OdeTimJsonProcessor.java
new file mode 100644
index 0000000..751ce8c
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/OdeTimJsonProcessor.java
@@ -0,0 +1,42 @@
+package us.dot.its.jpo.deduplicator.deduplicator.processors;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import us.dot.its.jpo.deduplicator.DeduplicatorProperties;
+
+public class OdeTimJsonProcessor extends DeduplicationProcessor{
+
+ DateTimeFormatter formatter = DateTimeFormatter.ISO_INSTANT;
+
+ DeduplicatorProperties props;
+ public OdeTimJsonProcessor(DeduplicatorProperties props){
+ this.props = props;
+ this.storeName = props.getKafkaStateStoreOdeTimJsonName();
+ }
+
+
+ @Override
+ public Instant getMessageTime(JsonNode message) {
+ try {
+ String time = message.get("metadata").get("odeReceivedAt").asText();
+ return Instant.from(formatter.parse(time));
+ } catch (Exception e) {
+ return Instant.ofEpochMilli(0);
+ }
+ }
+
+ @Override
+ public boolean isDuplicate(JsonNode lastMessage, JsonNode newMessage) {
+ Instant oldValueTime = getMessageTime(lastMessage);
+ Instant newValueTime = getMessageTime(newMessage);
+
+ if(newValueTime.minus(Duration.ofHours(1)).isAfter(oldValueTime)){
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/ProcessedMapProcessor.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/ProcessedMapProcessor.java
new file mode 100644
index 0000000..35e2c2f
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/ProcessedMapProcessor.java
@@ -0,0 +1,57 @@
+package us.dot.its.jpo.deduplicator.deduplicator.processors;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.time.ZonedDateTime;
+import java.util.Objects;
+
+import us.dot.its.jpo.deduplicator.DeduplicatorProperties;
+import us.dot.its.jpo.geojsonconverter.pojos.geojson.LineString;
+import us.dot.its.jpo.geojsonconverter.pojos.geojson.map.ProcessedMap;
+
+public class ProcessedMapProcessor extends DeduplicationProcessor>{
+
+ DeduplicatorProperties props;
+
+ public ProcessedMapProcessor(DeduplicatorProperties props){
+ this.props = props;
+ this.storeName = props.getKafkaStateStoreProcessedMapName();
+ }
+
+
+ @Override
+ public Instant getMessageTime(ProcessedMap message) {
+ try {
+ return message.getProperties().getOdeReceivedAt().toInstant();
+ } catch (Exception e) {
+ return Instant.ofEpochMilli(0);
+ }
+ }
+
+ @Override
+ public boolean isDuplicate(ProcessedMap lastMessage, ProcessedMap newMessage) {
+
+ Instant newValueTime = getMessageTime(newMessage);
+ Instant oldValueTime = getMessageTime(lastMessage);
+
+ if(newValueTime.minus(Duration.ofHours(1)).isAfter(oldValueTime)){
+ return false;
+ }else{
+ ZonedDateTime newValueTimestamp = newMessage.getProperties().getTimeStamp();
+ ZonedDateTime newValueOdeReceivedAt = newMessage.getProperties().getOdeReceivedAt();
+
+ newMessage.getProperties().setTimeStamp(lastMessage.getProperties().getTimeStamp());
+ newMessage.getProperties().setOdeReceivedAt(lastMessage.getProperties().getOdeReceivedAt());
+
+ int oldHash = Objects.hash(lastMessage.toString());
+ int newHash = Objects.hash(newMessage.toString());
+
+ if(oldHash != newHash){
+ newMessage.getProperties().setTimeStamp(newValueTimestamp);
+ newMessage.getProperties().setOdeReceivedAt(newValueOdeReceivedAt);
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/ProcessedMapWktProcessor.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/ProcessedMapWktProcessor.java
new file mode 100644
index 0000000..7556060
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/ProcessedMapWktProcessor.java
@@ -0,0 +1,55 @@
+package us.dot.its.jpo.deduplicator.deduplicator.processors;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.time.ZonedDateTime;
+import us.dot.its.jpo.deduplicator.DeduplicatorProperties;
+import us.dot.its.jpo.geojsonconverter.pojos.geojson.map.ProcessedMap;
+
+public class ProcessedMapWktProcessor extends DeduplicationProcessor>{
+
+ DeduplicatorProperties props;
+
+ public ProcessedMapWktProcessor(DeduplicatorProperties props){
+ this.props = props;
+ this.storeName = props.getKafkaStateStoreProcessedMapWKTName();
+ }
+
+
+ @Override
+ public Instant getMessageTime(ProcessedMap message) {
+ try {
+ return message.getProperties().getOdeReceivedAt().toInstant();
+ } catch (Exception e) {
+ return Instant.ofEpochMilli(0);
+ }
+ }
+
+ @Override
+ public boolean isDuplicate(ProcessedMap lastMessage, ProcessedMap newMessage) {
+
+ Instant newValueTime = newMessage.getProperties().getTimeStamp().toInstant();
+ Instant oldValueTime = lastMessage.getProperties().getTimeStamp().toInstant();
+
+ if(newValueTime.minus(Duration.ofHours(1)).isAfter(oldValueTime)){
+ return false;
+ }else{
+ ZonedDateTime newValueTimestamp = newMessage.getProperties().getTimeStamp();
+ ZonedDateTime newValueOdeReceivedAt = newMessage.getProperties().getOdeReceivedAt();
+
+ newMessage.getProperties().setTimeStamp(lastMessage.getProperties().getTimeStamp());
+ newMessage.getProperties().setOdeReceivedAt(lastMessage.getProperties().getOdeReceivedAt());
+
+ int oldHash = lastMessage.getProperties().hashCode();
+ int newhash = newMessage.getProperties().hashCode();
+
+ if(oldHash != newhash){
+ newMessage.getProperties().setTimeStamp(newValueTimestamp);
+ newMessage.getProperties().setOdeReceivedAt(newValueOdeReceivedAt);
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/ProcessedSpatProcessor.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/ProcessedSpatProcessor.java
new file mode 100644
index 0000000..2cc58af
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/ProcessedSpatProcessor.java
@@ -0,0 +1,64 @@
+package us.dot.its.jpo.deduplicator.deduplicator.processors;
+
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.HashMap;
+import java.util.List;
+
+import us.dot.its.jpo.deduplicator.DeduplicatorProperties;
+import us.dot.its.jpo.geojsonconverter.pojos.spat.MovementEvent;
+import us.dot.its.jpo.geojsonconverter.pojos.spat.MovementState;
+import us.dot.its.jpo.geojsonconverter.pojos.spat.ProcessedSpat;
+
+public class ProcessedSpatProcessor extends DeduplicationProcessor{
+
+ DeduplicatorProperties props;
+
+ public ProcessedSpatProcessor(DeduplicatorProperties props){
+ this.props = props;
+ this.storeName = props.getKafkaStateStoreProcessedSpatName();
+ }
+
+
+ @Override
+ public Instant getMessageTime(ProcessedSpat message) {
+ return message.getUtcTimeStamp().toInstant();
+ }
+
+ @Override
+ public boolean isDuplicate(ProcessedSpat lastMessage, ProcessedSpat newMessage) {
+
+ Instant newValueTime = getMessageTime(newMessage);
+ Instant oldValueTime = getMessageTime(lastMessage);
+
+ if(newValueTime.minus(Duration.ofMinutes(1)).isAfter(oldValueTime)){
+ return false;
+ }else{
+ HashMap> lastMessageStates = new HashMap<>();
+ for(MovementState state: lastMessage.getStates()){
+ lastMessageStates.put(state.getSignalGroup(), state.getStateTimeSpeed());
+ }
+
+ if(lastMessageStates.size() != newMessage.getStates().size()){
+ return false; // message cannot be duplicate if the signal groups have a different number of signal groups
+ }
+
+ for(MovementState state: newMessage.getStates()){
+ List lastMessageState = lastMessageStates.get(state.getSignalGroup());
+
+ if(lastMessageState == null){
+ return false; // messages cannot be duplicates if they have different signal groups
+ }
+
+
+ for(int i=0; i< state.getStateTimeSpeed().size(); i++){
+ if(state.getStateTimeSpeed().get(i).getEventState() != lastMessageState.get(i).getEventState()){
+ return false; // Some signal group light has changed. Therefore the SPaTs are different
+ }
+ }
+ }
+ }
+ return true;
+ }
+}
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/suppliers/OdeBsmJsonProcessorSupplier.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/suppliers/OdeBsmJsonProcessorSupplier.java
new file mode 100644
index 0000000..6b08b9f
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/suppliers/OdeBsmJsonProcessorSupplier.java
@@ -0,0 +1,26 @@
+package us.dot.its.jpo.deduplicator.deduplicator.processors.suppliers;
+
+import java.time.format.DateTimeFormatter;
+import org.apache.kafka.streams.processor.api.Processor;
+import org.apache.kafka.streams.processor.api.ProcessorSupplier;
+
+import us.dot.its.jpo.deduplicator.DeduplicatorProperties;
+import us.dot.its.jpo.deduplicator.deduplicator.processors.OdeBsmJsonProcessor;
+import us.dot.its.jpo.ode.model.OdeBsmData;
+
+public class OdeBsmJsonProcessorSupplier implements ProcessorSupplier {
+
+ String storeName;
+ DateTimeFormatter formatter = DateTimeFormatter.ISO_INSTANT;
+ DeduplicatorProperties props;
+
+ public OdeBsmJsonProcessorSupplier(String storeName, DeduplicatorProperties props){
+ this.storeName = storeName;
+ this.props = props;
+ }
+
+ @Override
+ public Processor get() {
+ return new OdeBsmJsonProcessor(storeName, props);
+ }
+}
\ No newline at end of file
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/suppliers/OdeMapJsonProcessorSupplier.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/suppliers/OdeMapJsonProcessorSupplier.java
new file mode 100644
index 0000000..829af13
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/suppliers/OdeMapJsonProcessorSupplier.java
@@ -0,0 +1,22 @@
+package us.dot.its.jpo.deduplicator.deduplicator.processors.suppliers;
+
+import org.apache.kafka.streams.processor.api.Processor;
+import org.apache.kafka.streams.processor.api.ProcessorSupplier;
+
+import us.dot.its.jpo.deduplicator.DeduplicatorProperties;
+import us.dot.its.jpo.deduplicator.deduplicator.processors.OdeMapJsonProcessor;
+import us.dot.its.jpo.ode.model.OdeMapData;
+
+public class OdeMapJsonProcessorSupplier implements ProcessorSupplier {
+
+ DeduplicatorProperties props;
+
+ public OdeMapJsonProcessorSupplier(DeduplicatorProperties props){
+ this.props = props;
+ }
+
+ @Override
+ public Processor get() {
+ return new OdeMapJsonProcessor(props);
+ }
+}
\ No newline at end of file
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/suppliers/OdeRawEncodedTimProcessorSupplier.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/suppliers/OdeRawEncodedTimProcessorSupplier.java
new file mode 100644
index 0000000..ef0ebb7
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/suppliers/OdeRawEncodedTimProcessorSupplier.java
@@ -0,0 +1,22 @@
+package us.dot.its.jpo.deduplicator.deduplicator.processors.suppliers;
+
+import org.apache.kafka.streams.processor.api.Processor;
+import org.apache.kafka.streams.processor.api.ProcessorSupplier;
+import com.fasterxml.jackson.databind.JsonNode;
+
+import us.dot.its.jpo.deduplicator.DeduplicatorProperties;
+import us.dot.its.jpo.deduplicator.deduplicator.processors.OdeRawEncodedTimJsonProcessor;
+
+public class OdeRawEncodedTimProcessorSupplier implements ProcessorSupplier {
+
+ DeduplicatorProperties props;
+
+ public OdeRawEncodedTimProcessorSupplier(DeduplicatorProperties props){
+ this.props = props;
+ }
+
+ @Override
+ public Processor get() {
+ return new OdeRawEncodedTimJsonProcessor(props);
+ }
+}
\ No newline at end of file
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/suppliers/OdeTimJsonProcessorSupplier.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/suppliers/OdeTimJsonProcessorSupplier.java
new file mode 100644
index 0000000..7a27588
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/suppliers/OdeTimJsonProcessorSupplier.java
@@ -0,0 +1,22 @@
+package us.dot.its.jpo.deduplicator.deduplicator.processors.suppliers;
+
+import org.apache.kafka.streams.processor.api.Processor;
+import org.apache.kafka.streams.processor.api.ProcessorSupplier;
+import com.fasterxml.jackson.databind.JsonNode;
+
+import us.dot.its.jpo.deduplicator.DeduplicatorProperties;
+import us.dot.its.jpo.deduplicator.deduplicator.processors.OdeTimJsonProcessor;
+
+public class OdeTimJsonProcessorSupplier implements ProcessorSupplier {
+
+ String storeName;
+ DeduplicatorProperties props;
+ public OdeTimJsonProcessorSupplier(DeduplicatorProperties props){
+ this.props = props;
+ }
+
+ @Override
+ public Processor get() {
+ return new OdeTimJsonProcessor(props);
+ }
+}
\ No newline at end of file
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/suppliers/ProcessedMapProcessorSupplier.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/suppliers/ProcessedMapProcessorSupplier.java
new file mode 100644
index 0000000..9e45cd0
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/suppliers/ProcessedMapProcessorSupplier.java
@@ -0,0 +1,23 @@
+package us.dot.its.jpo.deduplicator.deduplicator.processors.suppliers;
+
+import org.apache.kafka.streams.processor.api.Processor;
+import org.apache.kafka.streams.processor.api.ProcessorSupplier;
+
+import us.dot.its.jpo.deduplicator.DeduplicatorProperties;
+import us.dot.its.jpo.deduplicator.deduplicator.processors.ProcessedMapProcessor;
+import us.dot.its.jpo.geojsonconverter.pojos.geojson.LineString;
+import us.dot.its.jpo.geojsonconverter.pojos.geojson.map.ProcessedMap;
+
+public class ProcessedMapProcessorSupplier implements ProcessorSupplier, String, ProcessedMap> {
+
+ DeduplicatorProperties props;
+
+ public ProcessedMapProcessorSupplier(DeduplicatorProperties props){
+ this.props = props;
+ }
+
+ @Override
+ public Processor, String, ProcessedMap> get() {
+ return new ProcessedMapProcessor(props);
+ }
+}
\ No newline at end of file
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/suppliers/ProcessedMapWktProcessorSupplier.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/suppliers/ProcessedMapWktProcessorSupplier.java
new file mode 100644
index 0000000..3dbaa8c
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/suppliers/ProcessedMapWktProcessorSupplier.java
@@ -0,0 +1,22 @@
+package us.dot.its.jpo.deduplicator.deduplicator.processors.suppliers;
+
+import org.apache.kafka.streams.processor.api.Processor;
+import org.apache.kafka.streams.processor.api.ProcessorSupplier;
+
+import us.dot.its.jpo.deduplicator.DeduplicatorProperties;
+import us.dot.its.jpo.deduplicator.deduplicator.processors.ProcessedMapWktProcessor;
+import us.dot.its.jpo.geojsonconverter.pojos.geojson.map.ProcessedMap;
+
+public class ProcessedMapWktProcessorSupplier implements ProcessorSupplier, String, ProcessedMap> {
+
+ DeduplicatorProperties props;
+
+ public ProcessedMapWktProcessorSupplier(DeduplicatorProperties props){
+ this.props = props;
+ }
+
+ @Override
+ public Processor, String, ProcessedMap> get() {
+ return new ProcessedMapWktProcessor(props);
+ }
+}
\ No newline at end of file
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/suppliers/ProcessedSpatProcessorSupplier.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/suppliers/ProcessedSpatProcessorSupplier.java
new file mode 100644
index 0000000..e6661ac
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/processors/suppliers/ProcessedSpatProcessorSupplier.java
@@ -0,0 +1,22 @@
+package us.dot.its.jpo.deduplicator.deduplicator.processors.suppliers;
+
+import org.apache.kafka.streams.processor.api.Processor;
+import org.apache.kafka.streams.processor.api.ProcessorSupplier;
+
+import us.dot.its.jpo.deduplicator.DeduplicatorProperties;
+import us.dot.its.jpo.deduplicator.deduplicator.processors.ProcessedSpatProcessor;
+import us.dot.its.jpo.geojsonconverter.pojos.spat.ProcessedSpat;
+
+public class ProcessedSpatProcessorSupplier implements ProcessorSupplier {
+
+ DeduplicatorProperties props;
+
+ public ProcessedSpatProcessorSupplier(DeduplicatorProperties props){
+ this.props = props;
+ }
+
+ @Override
+ public Processor get() {
+ return new ProcessedSpatProcessor(props);
+ }
+}
\ No newline at end of file
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/serialization/JsonSerdes.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/serialization/JsonSerdes.java
new file mode 100644
index 0000000..8f65168
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/serialization/JsonSerdes.java
@@ -0,0 +1,24 @@
+package us.dot.its.jpo.deduplicator.deduplicator.serialization;
+
+import org.apache.kafka.common.serialization.Serde;
+import org.apache.kafka.common.serialization.Serdes;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import us.dot.its.jpo.geojsonconverter.serialization.deserializers.JsonDeserializer;
+import us.dot.its.jpo.geojsonconverter.serialization.serializers.JsonSerializer;
+import us.dot.its.jpo.ode.model.OdeTimData;
+
+public class JsonSerdes {
+ public static Serde Tim() {
+ return Serdes.serdeFrom(
+ new JsonSerializer(),
+ new JsonDeserializer<>(OdeTimData.class));
+ }
+
+ public static Serde JSON(){
+ return Serdes.serdeFrom(
+ new JsonSerializer(),
+ new JsonDeserializer<>(JsonNode.class));
+ }
+}
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/serialization/PairSerdes.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/serialization/PairSerdes.java
new file mode 100644
index 0000000..e509fa0
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/serialization/PairSerdes.java
@@ -0,0 +1,52 @@
+package us.dot.its.jpo.deduplicator.deduplicator.serialization;
+
+import org.apache.kafka.common.serialization.Serde;
+import org.apache.kafka.common.serialization.Serdes;
+
+import us.dot.its.jpo.deduplicator.deduplicator.models.ProcessedMapPair;
+import us.dot.its.jpo.deduplicator.deduplicator.models.ProcessedMapWktPair;
+import us.dot.its.jpo.deduplicator.deduplicator.models.OdeMapPair;
+import us.dot.its.jpo.deduplicator.deduplicator.models.OdeBsmPair;
+import us.dot.its.jpo.deduplicator.deduplicator.models.JsonPair;
+import us.dot.its.jpo.geojsonconverter.pojos.geojson.map.DeserializedRawMap;
+import us.dot.its.jpo.geojsonconverter.serialization.deserializers.JsonDeserializer;
+import us.dot.its.jpo.geojsonconverter.serialization.serializers.JsonSerializer;
+
+
+public class PairSerdes {
+ public static Serde ProcessedMapPair() {
+ return Serdes.serdeFrom(
+ new JsonSerializer(),
+ new JsonDeserializer<>(ProcessedMapPair.class));
+ }
+
+ public static Serde ProcessedMapWktPair() {
+ return Serdes.serdeFrom(
+ new JsonSerializer(),
+ new JsonDeserializer<>(ProcessedMapWktPair.class));
+ }
+
+ public static Serde OdeMapPair() {
+ return Serdes.serdeFrom(
+ new JsonSerializer(),
+ new JsonDeserializer<>(OdeMapPair.class));
+ }
+
+ public static Serde OdeBsmPair() {
+ return Serdes.serdeFrom(
+ new JsonSerializer(),
+ new JsonDeserializer<>(OdeBsmPair.class));
+ }
+
+ public static Serde RawMap() {
+ return Serdes.serdeFrom(
+ new JsonSerializer(),
+ new JsonDeserializer<>(DeserializedRawMap.class));
+ }
+
+ public static Serde JsonPair() {
+ return Serdes.serdeFrom(
+ new JsonSerializer(),
+ new JsonDeserializer<>(JsonPair.class));
+ }
+}
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/topologies/BsmDeduplicatorTopology.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/topologies/BsmDeduplicatorTopology.java
new file mode 100644
index 0000000..6b26d67
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/topologies/BsmDeduplicatorTopology.java
@@ -0,0 +1,125 @@
+package us.dot.its.jpo.deduplicator.deduplicator.topologies;
+
+import org.apache.kafka.common.serialization.Serdes;
+import org.apache.kafka.streams.KafkaStreams;
+import org.apache.kafka.streams.StreamsBuilder;
+import org.apache.kafka.streams.Topology;
+import org.apache.kafka.streams.KafkaStreams.StateListener;
+import org.apache.kafka.streams.errors.StreamsUncaughtExceptionHandler;
+
+import us.dot.its.jpo.conflictmonitor.monitor.serialization.JsonSerdes;
+import us.dot.its.jpo.deduplicator.DeduplicatorProperties;
+import us.dot.its.jpo.ode.model.OdeBsmData;
+import us.dot.its.jpo.ode.model.OdeBsmMetadata;
+import us.dot.its.jpo.ode.model.OdeMapData;
+import us.dot.its.jpo.ode.model.OdeMapPayload;
+import us.dot.its.jpo.ode.plugin.j2735.J2735Bsm;
+import us.dot.its.jpo.ode.plugin.j2735.J2735BsmCoreData;
+import org.apache.kafka.streams.kstream.*;
+import org.apache.kafka.streams.state.Stores;
+import org.geotools.referencing.GeodeticCalculator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
+import java.util.Objects;
+
+import us.dot.its.jpo.deduplicator.deduplicator.processors.suppliers.OdeBsmJsonProcessorSupplier;
+import us.dot.its.jpo.geojsonconverter.DateJsonMapper;
+
+public class BsmDeduplicatorTopology {
+
+ private static final Logger logger = LoggerFactory.getLogger(BsmDeduplicatorTopology.class);
+
+ Topology topology;
+ KafkaStreams streams;
+ DeduplicatorProperties props;
+ ObjectMapper objectMapper;
+ DateTimeFormatter formatter = DateTimeFormatter.ISO_INSTANT;
+ GeodeticCalculator calculator;
+
+
+
+ public BsmDeduplicatorTopology(DeduplicatorProperties props){
+ this.props = props;
+ this.objectMapper = DateJsonMapper.getInstance();
+ calculator = new GeodeticCalculator();
+ }
+
+
+
+ public void start() {
+ if (streams != null && streams.state().isRunningOrRebalancing()) {
+ throw new IllegalStateException("Start called while streams is already running.");
+ }
+ Topology topology = buildTopology();
+ streams = new KafkaStreams(topology, props.createStreamProperties("BsmDeduplicator"));
+ if (exceptionHandler != null) streams.setUncaughtExceptionHandler(exceptionHandler);
+ if (stateListener != null) streams.setStateListener(stateListener);
+ logger.info("Starting Bsm Deduplicator Topology");
+ streams.start();
+ }
+
+ public Instant getInstantFromBsm(OdeBsmData bsm){
+ String time = ((OdeBsmMetadata)bsm.getMetadata()).getOdeReceivedAt();
+
+ return Instant.from(formatter.parse(time));
+ }
+
+ public int hashMapMessage(OdeMapData map){
+ OdeMapPayload payload = (OdeMapPayload)map.getPayload();
+ return Objects.hash(payload.toJson());
+
+ }
+
+ public Topology buildTopology() {
+ StreamsBuilder builder = new StreamsBuilder();
+
+ KStream inputStream = builder.stream(this.props.getKafkaTopicOdeBsmJson(), Consumed.with(Serdes.Void(), JsonSerdes.OdeBsm()));
+
+ builder.addStateStore(Stores.keyValueStoreBuilder(Stores.persistentKeyValueStore(props.getKafkaStateStoreOdeBsmJsonName()),
+ Serdes.String(), JsonSerdes.OdeBsm()));
+
+ KStream bsmRekeyedStream = inputStream.selectKey((key, value)->{
+ J2735BsmCoreData core = ((J2735Bsm)value.getPayload().getData()).getCoreData();
+ return core.getId();
+ }).repartition(Repartitioned.with(Serdes.String(), JsonSerdes.OdeBsm()));
+
+ KStream deduplicatedStream = bsmRekeyedStream.process(new OdeBsmJsonProcessorSupplier(props.getKafkaStateStoreOdeBsmJsonName(), props), props.getKafkaStateStoreOdeBsmJsonName());
+
+
+ deduplicatedStream.to(this.props.getKafkaTopicDeduplicatedOdeBsmJson(), Produced.with(Serdes.String(), JsonSerdes.OdeBsm()));
+
+ return builder.build();
+
+ }
+
+ public void stop() {
+ logger.info("Stopping Bsm deduplicator Socket Broadcast Topology.");
+ if (streams != null) {
+ streams.close();
+ streams.cleanUp();
+ streams = null;
+ }
+ logger.info("Stopped Bsm deduplicator Socket Broadcast Topology.");
+ }
+
+ StateListener stateListener;
+ public void registerStateListener(StateListener stateListener) {
+ this.stateListener = stateListener;
+ }
+
+ StreamsUncaughtExceptionHandler exceptionHandler;
+ public void registerUncaughtExceptionHandler(StreamsUncaughtExceptionHandler exceptionHandler) {
+ this.exceptionHandler = exceptionHandler;
+ }
+
+ public double calculateGeodeticDistance(double lat1, double lon1, double lat2, double lon2) {
+ calculator.setStartingGeographicPoint(lon1, lat1);
+ calculator.setDestinationGeographicPoint(lon2, lat2);
+ return calculator.getOrthodromicDistance();
+ }
+}
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/topologies/MapDeduplicatorTopology.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/topologies/MapDeduplicatorTopology.java
new file mode 100644
index 0000000..c8ac7f6
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/topologies/MapDeduplicatorTopology.java
@@ -0,0 +1,121 @@
+package us.dot.its.jpo.deduplicator.deduplicator.topologies;
+
+import org.apache.kafka.common.serialization.Serdes;
+import org.apache.kafka.streams.KafkaStreams;
+import org.apache.kafka.streams.StreamsBuilder;
+import org.apache.kafka.streams.Topology;
+import org.apache.kafka.streams.KafkaStreams.StateListener;
+import org.apache.kafka.streams.errors.StreamsUncaughtExceptionHandler;
+
+import us.dot.its.jpo.deduplicator.DeduplicatorProperties;
+import us.dot.its.jpo.deduplicator.deduplicator.processors.suppliers.OdeMapJsonProcessorSupplier;
+import us.dot.its.jpo.geojsonconverter.DateJsonMapper;
+import us.dot.its.jpo.geojsonconverter.partitioner.RsuIntersectionKey;
+import us.dot.its.jpo.geojsonconverter.serialization.JsonSerdes;
+import us.dot.its.jpo.ode.model.OdeMapData;
+import us.dot.its.jpo.ode.model.OdeMapMetadata;
+import us.dot.its.jpo.ode.model.OdeMapPayload;
+import us.dot.its.jpo.ode.plugin.j2735.J2735IntersectionReferenceID;
+
+import org.apache.kafka.streams.kstream.*;
+import org.apache.kafka.streams.state.Stores;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
+import java.util.Objects;
+import java.util.Properties;
+
+public class MapDeduplicatorTopology {
+
+ private static final Logger logger = LoggerFactory.getLogger(MapDeduplicatorTopology.class);
+
+ Topology topology;
+ KafkaStreams streams;
+ String inputTopic;
+ String outputTopic;
+ Properties streamsProperties;
+ ObjectMapper objectMapper;
+ DateTimeFormatter formatter = DateTimeFormatter.ISO_INSTANT;
+ DeduplicatorProperties props;
+
+
+ public MapDeduplicatorTopology(DeduplicatorProperties props, Properties streamsProperties){
+ this.props = props;
+ this.streamsProperties = streamsProperties;
+ this.objectMapper = DateJsonMapper.getInstance();
+ }
+
+
+
+ public void start() {
+ if (streams != null && streams.state().isRunningOrRebalancing()) {
+ throw new IllegalStateException("Start called while streams is already running.");
+ }
+ Topology topology = buildTopology();
+ streams = new KafkaStreams(topology, streamsProperties);
+ if (exceptionHandler != null) streams.setUncaughtExceptionHandler(exceptionHandler);
+ if (stateListener != null) streams.setStateListener(stateListener);
+ logger.info("Starting Map Deduplicator Topology");
+ streams.start();
+ }
+
+ public Instant getInstantFromMap(OdeMapData map){
+ String time = ((OdeMapMetadata)map.getMetadata()).getOdeReceivedAt();
+
+ return Instant.from(formatter.parse(time));
+ }
+
+ public int hashMapMessage(OdeMapData map){
+ OdeMapPayload payload = (OdeMapPayload)map.getPayload();
+ return Objects.hash(payload.toJson());
+
+ }
+
+ public Topology buildTopology() {
+ StreamsBuilder builder = new StreamsBuilder();
+
+ KStream inputStream = builder.stream(props.getKafkaTopicOdeMapJson(), Consumed.with(Serdes.Void(), JsonSerdes.OdeMap()));
+
+ builder.addStateStore(Stores.keyValueStoreBuilder(Stores.persistentKeyValueStore(props.getKafkaStateStoreOdeMapJsonName()),
+ Serdes.String(), JsonSerdes.OdeMap()));
+
+ KStream mapRekeyedStream = inputStream.selectKey((key, value)->{
+ J2735IntersectionReferenceID intersectionId = ((OdeMapPayload)value.getPayload()).getMap().getIntersections().getIntersections().get(0).getId();
+ RsuIntersectionKey newKey = new RsuIntersectionKey();
+ newKey.setRsuId(((OdeMapMetadata)value.getMetadata()).getOriginIp());
+ newKey.setIntersectionReferenceID(intersectionId);
+ return newKey.toString();
+ }).repartition(Repartitioned.with(Serdes.String(), JsonSerdes.OdeMap()));
+
+ KStream deduplicatedStream = mapRekeyedStream.process(new OdeMapJsonProcessorSupplier(props), props.getKafkaStateStoreOdeMapJsonName());
+
+ deduplicatedStream.to(props.getKafkaTopicDeduplicatedOdeMapJson(), Produced.with(Serdes.String(), JsonSerdes.OdeMap()));
+
+ return builder.build();
+
+ }
+
+ public void stop() {
+ logger.info("Stopping Map Deduplicator Socket Broadcast Topology.");
+ if (streams != null) {
+ streams.close();
+ streams.cleanUp();
+ streams = null;
+ }
+ logger.info("Stopped Map Deduplicator Socket Broadcast Topology.");
+ }
+
+ StateListener stateListener;
+ public void registerStateListener(StateListener stateListener) {
+ this.stateListener = stateListener;
+ }
+
+ StreamsUncaughtExceptionHandler exceptionHandler;
+ public void registerUncaughtExceptionHandler(StreamsUncaughtExceptionHandler exceptionHandler) {
+ this.exceptionHandler = exceptionHandler;
+ }
+}
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/topologies/OdeRawEncodedTimDeduplicatorTopology.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/topologies/OdeRawEncodedTimDeduplicatorTopology.java
new file mode 100644
index 0000000..9431d6b
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/topologies/OdeRawEncodedTimDeduplicatorTopology.java
@@ -0,0 +1,131 @@
+package us.dot.its.jpo.deduplicator.deduplicator.topologies;
+
+import org.apache.kafka.common.serialization.Serdes;
+import org.apache.kafka.streams.KafkaStreams;
+import org.apache.kafka.streams.StreamsBuilder;
+import org.apache.kafka.streams.Topology;
+import org.apache.kafka.streams.KafkaStreams.StateListener;
+import org.apache.kafka.streams.errors.StreamsUncaughtExceptionHandler;
+
+import us.dot.its.jpo.deduplicator.DeduplicatorProperties;
+import us.dot.its.jpo.deduplicator.deduplicator.serialization.JsonSerdes;
+import us.dot.its.jpo.geojsonconverter.DateJsonMapper;
+
+import org.apache.kafka.streams.kstream.*;
+import org.apache.kafka.streams.state.Stores;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
+import java.util.Objects;
+import java.util.Properties;
+
+import us.dot.its.jpo.deduplicator.deduplicator.processors.suppliers.OdeRawEncodedTimProcessorSupplier;
+
+public class OdeRawEncodedTimDeduplicatorTopology {
+
+ private static final Logger logger = LoggerFactory.getLogger(OdeRawEncodedTimDeduplicatorTopology.class);
+
+ Topology topology;
+ KafkaStreams streams;
+ String inputTopic;
+ String outputTopic;
+ Properties streamsProperties;
+ ObjectMapper objectMapper;
+ DeduplicatorProperties props;
+ DateTimeFormatter formatter = DateTimeFormatter.ISO_INSTANT;
+
+ public OdeRawEncodedTimDeduplicatorTopology(DeduplicatorProperties props, Properties streamsProperties){
+ this.streamsProperties = streamsProperties;
+ this.objectMapper = DateJsonMapper.getInstance();
+ this.props = props;
+ }
+
+
+ public void start() {
+ if (streams != null && streams.state().isRunningOrRebalancing()) {
+ throw new IllegalStateException("Start called while streams is already running.");
+ }
+ Topology topology = buildTopology();
+ streams = new KafkaStreams(topology, streamsProperties);
+ if (exceptionHandler != null) streams.setUncaughtExceptionHandler(exceptionHandler);
+ if (stateListener != null) streams.setStateListener(stateListener);
+ logger.info("Starting Tim Deduplicator Topology");
+ streams.start();
+ }
+
+ public JsonNode genJsonNode(){
+ return objectMapper.createObjectNode();
+ }
+
+ public Instant getInstantFromJsonTim(JsonNode tim){
+ try{
+ String time = tim.get("metadata").get("odeReceivedAt").asText();
+ return Instant.from(formatter.parse(time));
+ }catch(Exception e){
+ System.out.println("Failed to parse time");
+ return Instant.ofEpochMilli(0);
+ }
+ }
+
+
+ public Topology buildTopology() {
+ StreamsBuilder builder = new StreamsBuilder();
+
+ KStream inputStream = builder.stream(props.getKafkaTopicOdeRawEncodedTimJson(), Consumed.with(Serdes.Void(), JsonSerdes.JSON()));
+
+ builder.addStateStore(Stores.keyValueStoreBuilder(Stores.persistentKeyValueStore(props.getKafkaStateStoreOdeRawEncodedTimJsonName()),
+ Serdes.String(), JsonSerdes.JSON()));
+
+ KStream timRekeyedStream = inputStream.selectKey((key, value)->{
+ try{
+
+ String messageBytes = value.get("payload")
+ .get("data")
+ .get("bytes").asText();
+
+ int hash = Objects.hash(messageBytes);
+
+ String rsuIP = value.get("metadata").get("originIp").asText();
+
+ String newKey = rsuIP + "_" + hash;
+ return newKey;
+ }catch(Exception e){
+ return "";
+ }
+ }).repartition(Repartitioned.with(Serdes.String(), JsonSerdes.JSON()));
+
+
+
+ KStream deduplicatedStream = timRekeyedStream.process(new OdeRawEncodedTimProcessorSupplier(props), props.getKafkaStateStoreOdeRawEncodedTimJsonName());
+
+ deduplicatedStream.to(props.getKafkaTopicDeduplicatedOdeRawEncodedTimJson(), Produced.with(Serdes.String(), JsonSerdes.JSON()));
+
+ return builder.build();
+
+ }
+
+ public void stop() {
+ logger.info("Stopping OdeRawEncodedTim Deduplicator Socket Broadcast Topology.");
+ if (streams != null) {
+ streams.close();
+ streams.cleanUp();
+ streams = null;
+ }
+ logger.info("Stopped OdeRawEncodedTim Deduplicator Socket Broadcast Topology.");
+ }
+
+ StateListener stateListener;
+ public void registerStateListener(StateListener stateListener) {
+ this.stateListener = stateListener;
+ }
+
+ StreamsUncaughtExceptionHandler exceptionHandler;
+ public void registerUncaughtExceptionHandler(StreamsUncaughtExceptionHandler exceptionHandler) {
+ this.exceptionHandler = exceptionHandler;
+ }
+}
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/topologies/ProcessedMapDeduplicatorTopology.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/topologies/ProcessedMapDeduplicatorTopology.java
new file mode 100644
index 0000000..5f9da3d
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/topologies/ProcessedMapDeduplicatorTopology.java
@@ -0,0 +1,92 @@
+package us.dot.its.jpo.deduplicator.deduplicator.topologies;
+
+import org.apache.kafka.common.serialization.Serdes;
+import org.apache.kafka.streams.KafkaStreams;
+import org.apache.kafka.streams.StreamsBuilder;
+import org.apache.kafka.streams.Topology;
+import org.apache.kafka.streams.KafkaStreams.StateListener;
+import org.apache.kafka.streams.errors.StreamsUncaughtExceptionHandler;
+
+import us.dot.its.jpo.deduplicator.DeduplicatorProperties;
+import us.dot.its.jpo.deduplicator.deduplicator.processors.suppliers.ProcessedMapProcessorSupplier;
+import us.dot.its.jpo.geojsonconverter.DateJsonMapper;
+import us.dot.its.jpo.geojsonconverter.pojos.geojson.LineString;
+import us.dot.its.jpo.geojsonconverter.pojos.geojson.map.ProcessedMap;
+import us.dot.its.jpo.geojsonconverter.serialization.JsonSerdes;
+import org.apache.kafka.streams.kstream.*;
+import org.apache.kafka.streams.state.Stores;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+
+import java.util.Properties;
+
+public class ProcessedMapDeduplicatorTopology {
+
+ private static final Logger logger = LoggerFactory.getLogger(MapDeduplicatorTopology.class);
+
+ Topology topology;
+ KafkaStreams streams;
+ Properties streamsProperties;
+ ObjectMapper objectMapper;
+ DeduplicatorProperties props;
+
+ public ProcessedMapDeduplicatorTopology(DeduplicatorProperties props, Properties streamsProperties){
+ this.props = props;
+ this.streamsProperties = streamsProperties;
+ this.objectMapper = DateJsonMapper.getInstance();
+ }
+
+
+ public void start() {
+ if (streams != null && streams.state().isRunningOrRebalancing()) {
+ throw new IllegalStateException("Start called while streams is already running.");
+ }
+ Topology topology = buildTopology();
+ streams = new KafkaStreams(topology, streamsProperties);
+ if (exceptionHandler != null) streams.setUncaughtExceptionHandler(exceptionHandler);
+ if (stateListener != null) streams.setStateListener(stateListener);
+ logger.info("Starting Processed Map Deduplicator Topology");
+ streams.start();
+ }
+
+ public Topology buildTopology() {
+ StreamsBuilder builder = new StreamsBuilder();
+
+ KStream> inputStream = builder.stream(props.getKafkaTopicProcessedMap(), Consumed.with(Serdes.String(), JsonSerdes.ProcessedMapGeoJson()));
+
+ builder.addStateStore(Stores.keyValueStoreBuilder(Stores.persistentKeyValueStore(props.getKafkaStateStoreProcessedMapName()),
+ Serdes.String(), JsonSerdes.ProcessedMapGeoJson()));
+
+ KStream> deduplicatedStream = inputStream.process(new ProcessedMapProcessorSupplier(props), props.getKafkaStateStoreProcessedMapName());
+
+ deduplicatedStream.to(props.getKafkaTopicDeduplicatedProcessedMap(), Produced.with(Serdes.String(), JsonSerdes.ProcessedMapGeoJson()));
+
+ return builder.build();
+
+ }
+
+ public void stop() {
+ logger.info("Stopping Processed Map deduplicator Socket Broadcast Topology.");
+ if (streams != null) {
+ streams.close();
+ streams.cleanUp();
+ streams = null;
+ }
+ logger.info("Stopped Processed Map deduplicator Socket Broadcast Topology.");
+ }
+
+ StateListener stateListener;
+ public void registerStateListener(StateListener stateListener) {
+ this.stateListener = stateListener;
+ }
+
+ StreamsUncaughtExceptionHandler exceptionHandler;
+ public void registerUncaughtExceptionHandler(StreamsUncaughtExceptionHandler exceptionHandler) {
+ this.exceptionHandler = exceptionHandler;
+ }
+
+
+
+}
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/topologies/ProcessedMapWktDeduplicatorTopology.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/topologies/ProcessedMapWktDeduplicatorTopology.java
new file mode 100644
index 0000000..872e66f
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/topologies/ProcessedMapWktDeduplicatorTopology.java
@@ -0,0 +1,93 @@
+package us.dot.its.jpo.deduplicator.deduplicator.topologies;
+
+import org.apache.kafka.common.serialization.Serdes;
+import org.apache.kafka.streams.KafkaStreams;
+import org.apache.kafka.streams.StreamsBuilder;
+import org.apache.kafka.streams.Topology;
+import org.apache.kafka.streams.KafkaStreams.StateListener;
+import org.apache.kafka.streams.errors.StreamsUncaughtExceptionHandler;
+
+import us.dot.its.jpo.deduplicator.DeduplicatorProperties;
+import us.dot.its.jpo.deduplicator.deduplicator.processors.suppliers.ProcessedMapWktProcessorSupplier;
+import us.dot.its.jpo.geojsonconverter.DateJsonMapper;
+import us.dot.its.jpo.geojsonconverter.pojos.geojson.map.ProcessedMap;
+import us.dot.its.jpo.geojsonconverter.serialization.JsonSerdes;
+
+import org.apache.kafka.streams.kstream.*;
+import org.apache.kafka.streams.state.Stores;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.util.Properties;
+
+public class ProcessedMapWktDeduplicatorTopology {
+
+ private static final Logger logger = LoggerFactory.getLogger(MapDeduplicatorTopology.class);
+
+ Topology topology;
+ KafkaStreams streams;
+ String inputTopic;
+ String outputTopic;
+ Properties streamsProperties;
+ ObjectMapper objectMapper;
+ DeduplicatorProperties props;
+
+ public ProcessedMapWktDeduplicatorTopology(DeduplicatorProperties props, Properties streamsProperties){
+ this.props = props;
+ this.streamsProperties = streamsProperties;
+ this.objectMapper = DateJsonMapper.getInstance();
+ }
+
+
+ public void start() {
+ if (streams != null && streams.state().isRunningOrRebalancing()) {
+ throw new IllegalStateException("Start called while streams is already running.");
+ }
+ Topology topology = buildTopology();
+ streams = new KafkaStreams(topology, streamsProperties);
+ if (exceptionHandler != null) streams.setUncaughtExceptionHandler(exceptionHandler);
+ if (stateListener != null) streams.setStateListener(stateListener);
+ logger.info("Starting Processed Map WKT Deduplicator Topology");
+ streams.start();
+ }
+
+ public Topology buildTopology() {
+ StreamsBuilder builder = new StreamsBuilder();
+
+ KStream> inputStream = builder.stream(props.getKafkaTopicProcessedMapWKT(), Consumed.with(Serdes.String(), JsonSerdes.ProcessedMapWKT()));
+
+ builder.addStateStore(Stores.keyValueStoreBuilder(Stores.persistentKeyValueStore(props.getKafkaStateStoreProcessedMapWKTName()),
+ Serdes.String(), JsonSerdes.ProcessedMapWKT()));
+
+ KStream> deduplicatedStream = inputStream.process(new ProcessedMapWktProcessorSupplier(props), props.getKafkaStateStoreProcessedMapWKTName());
+
+ deduplicatedStream.to(props.getKafkaTopicDeduplicatedProcessedMapWKT(), Produced.with(Serdes.String(), JsonSerdes.ProcessedMapWKT()));
+
+ return builder.build();
+
+ }
+
+ public void stop() {
+ logger.info("Stopping Processed Map deduplicator Socket Broadcast Topology.");
+ if (streams != null) {
+ streams.close();
+ streams.cleanUp();
+ streams = null;
+ }
+ logger.info("Stopped Processed Map deduplicator Socket Broadcast Topology.");
+ }
+
+ StateListener stateListener;
+ public void registerStateListener(StateListener stateListener) {
+ this.stateListener = stateListener;
+ }
+
+ StreamsUncaughtExceptionHandler exceptionHandler;
+ public void registerUncaughtExceptionHandler(StreamsUncaughtExceptionHandler exceptionHandler) {
+ this.exceptionHandler = exceptionHandler;
+ }
+
+
+
+}
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/topologies/ProcessedSpatDeduplicatorTopology.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/topologies/ProcessedSpatDeduplicatorTopology.java
new file mode 100644
index 0000000..b478669
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/topologies/ProcessedSpatDeduplicatorTopology.java
@@ -0,0 +1,97 @@
+package us.dot.its.jpo.deduplicator.deduplicator.topologies;
+
+import org.apache.kafka.common.serialization.Serdes;
+import org.apache.kafka.streams.KafkaStreams;
+import org.apache.kafka.streams.StreamsBuilder;
+import org.apache.kafka.streams.Topology;
+import org.apache.kafka.streams.KafkaStreams.StateListener;
+import org.apache.kafka.streams.errors.StreamsUncaughtExceptionHandler;
+
+import us.dot.its.jpo.deduplicator.DeduplicatorProperties;
+import us.dot.its.jpo.geojsonconverter.DateJsonMapper;
+import us.dot.its.jpo.deduplicator.deduplicator.processors.suppliers.ProcessedSpatProcessorSupplier;
+import us.dot.its.jpo.geojsonconverter.pojos.spat.ProcessedSpat;
+import us.dot.its.jpo.geojsonconverter.serialization.JsonSerdes;
+
+import org.apache.kafka.streams.kstream.*;
+import org.apache.kafka.streams.state.Stores;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.time.format.DateTimeFormatter;
+import java.util.Properties;
+
+public class ProcessedSpatDeduplicatorTopology {
+
+ private static final Logger logger = LoggerFactory.getLogger(ProcessedSpatDeduplicatorTopology.class);
+
+ Topology topology;
+ KafkaStreams streams;
+ String inputTopic;
+ String outputTopic;
+ Properties streamsProperties;
+ ObjectMapper objectMapper;
+ DateTimeFormatter formatter = DateTimeFormatter.ISO_INSTANT;
+ DeduplicatorProperties props;
+
+
+ public ProcessedSpatDeduplicatorTopology(DeduplicatorProperties props, Properties streamsProperties){
+ this.props = props;
+ this.streamsProperties = streamsProperties;
+ this.objectMapper = DateJsonMapper.getInstance();
+ }
+
+
+
+ public void start() {
+ if (streams != null && streams.state().isRunningOrRebalancing()) {
+ throw new IllegalStateException("Start called while streams is already running.");
+ }
+ Topology topology = buildTopology();
+ streams = new KafkaStreams(topology, streamsProperties);
+ if (exceptionHandler != null) streams.setUncaughtExceptionHandler(exceptionHandler);
+ if (stateListener != null) streams.setStateListener(stateListener);
+ logger.info("Starting Map Deduplicator Topology");
+ streams.start();
+ }
+
+ public Topology buildTopology() {
+ StreamsBuilder builder = new StreamsBuilder();
+
+ KStream inputStream = builder.stream(props.getKafkaTopicProcessedSpat(), Consumed.with(Serdes.String(), JsonSerdes.ProcessedSpat()));
+
+ builder.addStateStore(Stores.keyValueStoreBuilder(Stores.persistentKeyValueStore(props.getKafkaStateStoreProcessedSpatName()),
+ Serdes.String(), JsonSerdes.ProcessedSpat()));
+
+ KStream deduplicatedStream = inputStream.process(new ProcessedSpatProcessorSupplier(props), props.getKafkaStateStoreProcessedSpatName());
+
+ deduplicatedStream.print(Printed.toSysOut());
+
+ deduplicatedStream.to(props.getKafkaTopicDeduplicatedProcessedSpat(), Produced.with(Serdes.String(), JsonSerdes.ProcessedSpat()));
+
+ return builder.build();
+
+ }
+
+ public void stop() {
+ logger.info("Stopping Processed SPaT deduplicator Socket Broadcast Topology.");
+ if (streams != null) {
+ streams.close();
+ streams.cleanUp();
+ streams = null;
+ }
+ logger.info("Stopped Processed SPaT deduplicator Socket Broadcast Topology.");
+ }
+
+ StateListener stateListener;
+ public void registerStateListener(StateListener stateListener) {
+ this.stateListener = stateListener;
+ }
+
+ StreamsUncaughtExceptionHandler exceptionHandler;
+ public void registerUncaughtExceptionHandler(StreamsUncaughtExceptionHandler exceptionHandler) {
+ this.exceptionHandler = exceptionHandler;
+ }
+}
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/topologies/TimDeduplicatorTopology.java b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/topologies/TimDeduplicatorTopology.java
new file mode 100644
index 0000000..d8cf570
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/java/us/dot/its/jpo/deduplicator/deduplicator/topologies/TimDeduplicatorTopology.java
@@ -0,0 +1,126 @@
+package us.dot.its.jpo.deduplicator.deduplicator.topologies;
+
+import org.apache.kafka.common.serialization.Serdes;
+import org.apache.kafka.streams.KafkaStreams;
+import org.apache.kafka.streams.StreamsBuilder;
+import org.apache.kafka.streams.Topology;
+import org.apache.kafka.streams.KafkaStreams.StateListener;
+import org.apache.kafka.streams.errors.StreamsUncaughtExceptionHandler;
+
+import us.dot.its.jpo.deduplicator.deduplicator.serialization.JsonSerdes;
+import us.dot.its.jpo.geojsonconverter.DateJsonMapper;
+
+import org.apache.kafka.streams.kstream.*;
+import org.apache.kafka.streams.state.Stores;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.time.format.DateTimeFormatter;
+import java.util.Properties;
+
+import us.dot.its.jpo.deduplicator.DeduplicatorProperties;
+import us.dot.its.jpo.deduplicator.deduplicator.processors.suppliers.OdeTimJsonProcessorSupplier;
+
+
+public class TimDeduplicatorTopology {
+
+ private static final Logger logger = LoggerFactory.getLogger(TimDeduplicatorTopology.class);
+
+ Topology topology;
+ KafkaStreams streams;
+ Properties streamsProperties;
+ ObjectMapper objectMapper;
+ DateTimeFormatter formatter = DateTimeFormatter.ISO_INSTANT;
+ DeduplicatorProperties props;
+
+ public TimDeduplicatorTopology(DeduplicatorProperties props, Properties streamsProperties) {
+ this.props = props;
+ this.streamsProperties = streamsProperties;
+ this.objectMapper = DateJsonMapper.getInstance();
+ }
+
+ public void start() {
+ if (streams != null && streams.state().isRunningOrRebalancing()) {
+ throw new IllegalStateException("Start called while streams is already running.");
+ }
+ Topology topology = buildTopology();
+ streams = new KafkaStreams(topology, streamsProperties);
+ if (exceptionHandler != null)
+ streams.setUncaughtExceptionHandler(exceptionHandler);
+ if (stateListener != null)
+ streams.setStateListener(stateListener);
+ logger.info("Starting Tim Deduplicator Topology");
+ streams.start();
+ }
+
+ public JsonNode genJsonNode() {
+ return objectMapper.createObjectNode();
+ }
+
+
+
+ public Topology buildTopology() {
+ StreamsBuilder builder = new StreamsBuilder();
+
+ KStream inputStream = builder.stream(props.getKafkaTopicOdeTimJson(),
+ Consumed.with(Serdes.Void(), JsonSerdes.JSON()));
+
+ builder.addStateStore(Stores.keyValueStoreBuilder(Stores.persistentKeyValueStore(props.getKafkaStateStoreOdeTimJsonName()),
+ Serdes.String(), JsonSerdes.JSON()));
+
+
+
+ KStream timRekeyedStream = inputStream.selectKey((key, value) -> {
+ try {
+
+ JsonNode travellerInformation = value.get("payload")
+ .get("data")
+ .get("MessageFrame")
+ .get("value")
+ .get("TravelerInformation");
+
+ String rsuIP = value.get("metadata").get("originIp").asText();
+ String packetId = travellerInformation.get("packetID").asText();
+ String msgCnt = travellerInformation.get("msgCnt").asText();
+
+ String newKey = rsuIP + "_" + packetId + "_" + msgCnt;
+ return newKey;
+ } catch (Exception e) {
+ return "";
+ }
+ }).repartition(Repartitioned.with(Serdes.String(), JsonSerdes.JSON()));
+
+ KStream deduplicatedStream = timRekeyedStream.process(new OdeTimJsonProcessorSupplier(props), props.getKafkaStateStoreOdeTimJsonName());
+
+ deduplicatedStream.to(props.getKafkaTopicDeduplicatedOdeTimJson(), Produced.with(Serdes.String(), JsonSerdes.JSON()));
+
+ return builder.build();
+
+ }
+
+ public void stop() {
+ logger.info("Stopping Tim deduplicator Socket Broadcast Topology.");
+ if (streams != null) {
+ streams.close();
+ streams.cleanUp();
+ streams = null;
+ }
+ logger.info("Stopped Tim deduplicator Socket Broadcast Topology.");
+ }
+
+ StateListener stateListener;
+
+ public void registerStateListener(StateListener stateListener) {
+ this.stateListener = stateListener;
+ }
+
+ StreamsUncaughtExceptionHandler exceptionHandler;
+
+ public void registerUncaughtExceptionHandler(StreamsUncaughtExceptionHandler exceptionHandler) {
+ this.exceptionHandler = exceptionHandler;
+ }
+
+}
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/resources/application.yaml b/jpo-deduplicator/jpo-deduplicator/src/main/resources/application.yaml
new file mode 100644
index 0000000..9e6b89d
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/resources/application.yaml
@@ -0,0 +1,96 @@
+#General Properties
+#==================
+groupId: ^project.groupId^
+artifactId: ^project.artifactId^
+version: ^project.version^
+server.port: 8085
+
+# Kafka properties
+spring.kafka.bootstrap-servers: ${KAFKA_BOOTSTRAP_SERVERS:localhost:9092}
+logging.level.org.apache.kafka: INFO
+logging.level: INFO
+log4j.logger.kafka: OFF
+log4j.logger.org.apache.kafka: OFF
+
+
+
+# Processed Map Configuration
+kafkaTopicProcessedMap: topic.ProcessedMap
+kafkaTopicDeduplicatedProcessedMap: topic.DeduplicatedProcessedMap
+enableProcessedMapDeduplication: false
+
+# Processed Map WKT Configuration
+kafkaTopicProcessedMapWKT: topic.ProcessedMapWKT
+kafkaTopicDeduplicatedProcessedMapWKT: topic.DeduplicatedProcessedMapWKT
+enableProcessedMapWktDeduplication: false
+
+# Ode Map Json Configuration
+kafkaTopicOdeMapJson: topic.OdeMapJson
+kafkaTopicDeduplicatedOdeMapJson: topic.DeduplicatedOdeMapJson
+enableOdeMapDeduplication: false
+
+# Ode Tim Json Configuration
+kafkaTopicOdeTimJson: topic.OdeTimJson
+kafkaTopicDeduplicatedOdeTimJson: topic.DeduplicatedOdeTimJson
+enableOdeTimDeduplication: false
+
+# Ode Raw Encoded Tim Json Configuration
+kafkaTopicOdeRawEncodedTimJson: topic.OdeRawEncodedTIMJson
+kafkaTopicDeduplicatedOdeRawEncodedTimJson: topic.DeduplicatedOdeRawEncodedTIMJson
+enableOdeRawEncodedTimDeduplication: false
+
+# Ode Bsm Json Configuration
+kafkaTopicOdeBsmJson: topic.OdeBsmJson
+kafkaTopicDeduplicatedOdeBsmJson: topic.DeduplicatedOdeBsmJson
+enableOdeBsmDeduplication: false
+odeBsmMaximumTimeDelta: 10000 # Milliseconds
+odeBsmMaximumPositionDelta: 1 # Meter
+odeBsmAlwaysIncludeAtSpeed: 1 # Meter / Second
+
+# Processed Map Configuration
+kafkaTopicProcessedSpat: topic.ProcessedSpat
+kafkaTopicDeduplicatedProcessedSpat: topic.DeduplicatedProcessedSpat
+enableProcessedSpatDeduplication: false
+
+
+# Amount of time to wait to try and increase batching
+kafka.linger_ms: 50
+
+# Custom kafka properties
+kafka.topics:
+ autoCreateTopics: true # Override auto-create in test properties
+ numPartitions: 1
+ numReplicas: 1
+ createTopics:
+ - name: ${kafkaTopicProcessedMap}
+ cleanupPolicy: delete
+ retentionMs: 300000
+ - name: ${kafkaTopicDeduplicatedProcessedMap}
+ cleanupPolicy: delete
+ retentionMs: 300000
+ - name: ${kafkaTopicProcessedMapWKT}
+ cleanupPolicy: delete
+ retentionMs: 300000
+ - name: ${kafkaTopicDeduplicatedProcessedMapWKT}
+ cleanupPolicy: delete
+ retentionMs: 300000
+ - name: ${kafkaTopicOdeMapJson}
+ cleanupPolicy: delete
+ retentionMs: 300000
+ - name: ${kafkaTopicDeduplicatedOdeMapJson}
+ cleanupPolicy: delete
+ retentionMs: 300000
+ - name: ${kafkaTopicOdeTimJson}
+ cleanupPolicy: delete
+ retentionMs: 300000
+ - name: ${kafkaTopicDeduplicatedOdeTimJson}
+ cleanupPolicy: delete
+ retentionMs: 300000
+ - name: ${kafkaTopicProcessedSpat}
+ cleanupPolicy: delete
+ retentionMs: 300000
+ - name: ${kafkaTopicDeduplicatedProcessedSpat}
+ cleanupPolicy: delete
+ retentionMs: 300000
+
+
\ No newline at end of file
diff --git a/jpo-deduplicator/jpo-deduplicator/src/main/resources/logback.xml b/jpo-deduplicator/jpo-deduplicator/src/main/resources/logback.xml
new file mode 100644
index 0000000..860df15
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/main/resources/logback.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+ %date{"yyyy-MM-dd HH:mm:ss", UTC} [%thread] %-5level %logger{0} - %msg %n
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jpo-deduplicator/jpo-deduplicator/src/test/java/deduplicator/BsmDeduplicatorTopologyTest.java b/jpo-deduplicator/jpo-deduplicator/src/test/java/deduplicator/BsmDeduplicatorTopologyTest.java
new file mode 100644
index 0000000..98d6204
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/test/java/deduplicator/BsmDeduplicatorTopologyTest.java
@@ -0,0 +1,116 @@
+package deduplicator;
+
+
+
+import org.apache.kafka.common.serialization.Serdes;
+import org.apache.kafka.streams.KeyValue;
+import org.apache.kafka.streams.TestInputTopic;
+import org.apache.kafka.streams.TestOutputTopic;
+import org.apache.kafka.streams.Topology;
+import org.apache.kafka.streams.TopologyTestDriver;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import us.dot.its.jpo.conflictmonitor.monitor.serialization.JsonSerdes;
+import us.dot.its.jpo.deduplicator.DeduplicatorProperties;
+import us.dot.its.jpo.deduplicator.deduplicator.topologies.BsmDeduplicatorTopology;
+import us.dot.its.jpo.ode.model.OdeBsmData;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.List;
+
+
+public class BsmDeduplicatorTopologyTest {
+
+ String inputTopic = "topic.OdeBsmJson";
+ String outputTopic = "topic.DeduplicatedOdeBsmJson";
+ ObjectMapper objectMapper;
+
+ String inputBsm1 = "{\"metadata\":{\"bsmSource\":\"RV\",\"logFileName\":\"\",\"recordType\":\"bsmTx\",\"securityResultCode\":\"success\",\"receivedMessageDetails\":{\"locationData\":{\"latitude\":\"\",\"longitude\":\"\",\"elevation\":\"\",\"speed\":\"\",\"heading\":\"\"},\"rxSource\":\"RV\"},\"encodings\":null,\"payloadType\":\"us.dot.its.jpo.ode.model.OdeBsmPayload\",\"serialId\":{\"streamId\":\"f1bfed26-d986-4a0c-b8a4-68b1e5ac1348\",\"bundleSize\":1,\"bundleId\":0,\"recordId\":0,\"serialNumber\":0},\"odeReceivedAt\":\"2024-07-01T15:00:52.597Z\",\"schemaVersion\":6,\"maxDurationTime\":0,\"recordGeneratedAt\":\"\",\"recordGeneratedBy\":null,\"sanitized\":false,\"odePacketID\":\"\",\"odeTimStartDateTime\":\"\",\"originIp\":\"10.164.6.18\"},\"payload\":{\"data\":{\"coreData\":{\"msgCnt\":7,\"id\":\"E79423A3\",\"secMark\":52597,\"position\":{\"latitude\":40.2970849,\"longitude\":-111.6956069,\"elevation\":1439},\"accelSet\":{\"accelLat\":2001,\"accelLong\":0,\"accelVert\":-127,\"accelYaw\":0},\"accuracy\":{\"semiMajor\":2,\"semiMinor\":2,\"orientation\":44.49530799},\"transmission\":\"FORWARDGEARS\",\"speed\":0,\"heading\":24.2,\"angle\":0,\"brakes\":{\"wheelBrakes\":{\"leftFront\":true,\"rightFront\":true,\"unavailable\":false,\"leftRear\":true,\"rightRear\":true},\"traction\":\"on\",\"abs\":\"on\",\"scs\":\"on\",\"brakeBoost\":\"off\",\"auxBrakes\":\"unavailable\"},\"size\":{\"width\":230,\"length\":500}},\"partII\":[{\"id\":\"VehicleSafetyExtensions\",\"value\":{\"events\":null,\"pathHistory\":{\"initialPosition\":null,\"currGNSSstatus\":null,\"crumbData\":[{\"elevationOffset\":-0.5,\"heading\":null,\"latOffset\":0.0000038,\"lonOffset\":0.0001137,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":31.41},{\"elevationOffset\":-0.4,\"heading\":null,\"latOffset\":0.0000339,\"lonOffset\":0.0001695,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":32.05},{\"elevationOffset\":0,\"heading\":null,\"latOffset\":0.000184,\"lonOffset\":0.0002106,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":36.29},{\"elevationOffset\":0,\"heading\":null,\"latOffset\":0.0003092,\"lonOffset\":0.0003081,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":37.22},{\"elevationOffset\":0.4,\"heading\":null,\"latOffset\":0.0004354,\"lonOffset\":0.0003906,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":38.15},{\"elevationOffset\":1.4,\"heading\":null,\"latOffset\":0.0007727,\"lonOffset\":0.0004391,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":40.39},{\"elevationOffset\":1.4,\"heading\":null,\"latOffset\":0.00084,\"lonOffset\":0.0004778,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":41.61},{\"elevationOffset\":1.4,\"heading\":null,\"latOffset\":0.0008649,\"lonOffset\":0.0005765,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":43.48},{\"elevationOffset\":1.7,\"heading\":null,\"latOffset\":0.0008086,\"lonOffset\":0.0015482,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":68.53},{\"elevationOffset\":1.7,\"heading\":null,\"latOffset\":0.0007738,\"lonOffset\":0.0015944,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":72.09},{\"elevationOffset\":2.1,\"heading\":null,\"latOffset\":0.0007349,\"lonOffset\":0.0015747,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":76.97}]},\"pathPrediction\":{\"confidence\":100,\"radiusOfCurve\":0},\"lights\":null}},{\"id\":\"SupplementalVehicleExtensions\",\"value\":{\"classification\":66,\"classDetails\":{\"fuelType\":null,\"hpmsType\":\"none\",\"iso3883\":null,\"keyType\":66,\"responderType\":null,\"responseEquip\":null,\"role\":null,\"vehicleType\":null},\"vehicleData\":{\"bumpers\":null,\"height\":1.8,\"mass\":2800,\"trailerWeight\":null},\"weatherReport\":{\"friction\":null,\"isRaining\":\"ERROR\",\"precipSituation\":\"UNKNOWN\",\"rainRate\":null,\"roadFriction\":0,\"solarRadiation\":null},\"weatherProbe\":{\"airPressure\":860,\"airTemp\":71,\"rainRates\":null},\"obstacle\":null,\"status\":null,\"speedProfile\":null,\"theRTCM\":null}}]},\"dataType\":\"us.dot.its.jpo.ode.plugin.j2735.J2735Bsm\"}}";
+
+ // Same as BSM 1 - No Message should be generated
+ String inputBsm2 = "{\"metadata\":{\"bsmSource\":\"RV\",\"logFileName\":\"\",\"recordType\":\"bsmTx\",\"securityResultCode\":\"success\",\"receivedMessageDetails\":{\"locationData\":{\"latitude\":\"\",\"longitude\":\"\",\"elevation\":\"\",\"speed\":\"\",\"heading\":\"\"},\"rxSource\":\"RV\"},\"encodings\":null,\"payloadType\":\"us.dot.its.jpo.ode.model.OdeBsmPayload\",\"serialId\":{\"streamId\":\"f1bfed26-d986-4a0c-b8a4-68b1e5ac1348\",\"bundleSize\":1,\"bundleId\":0,\"recordId\":0,\"serialNumber\":0},\"odeReceivedAt\":\"2024-07-01T15:00:52.697Z\",\"schemaVersion\":6,\"maxDurationTime\":0,\"recordGeneratedAt\":\"\",\"recordGeneratedBy\":null,\"sanitized\":false,\"odePacketID\":\"\",\"odeTimStartDateTime\":\"\",\"originIp\":\"10.164.6.18\"},\"payload\":{\"data\":{\"coreData\":{\"msgCnt\":7,\"id\":\"E79423A3\",\"secMark\":52597,\"position\":{\"latitude\":40.2970849,\"longitude\":-111.6956069,\"elevation\":1439},\"accelSet\":{\"accelLat\":2001,\"accelLong\":0,\"accelVert\":-127,\"accelYaw\":0},\"accuracy\":{\"semiMajor\":2,\"semiMinor\":2,\"orientation\":44.49530799},\"transmission\":\"FORWARDGEARS\",\"speed\":0,\"heading\":24.2,\"angle\":0,\"brakes\":{\"wheelBrakes\":{\"leftFront\":true,\"rightFront\":true,\"unavailable\":false,\"leftRear\":true,\"rightRear\":true},\"traction\":\"on\",\"abs\":\"on\",\"scs\":\"on\",\"brakeBoost\":\"off\",\"auxBrakes\":\"unavailable\"},\"size\":{\"width\":230,\"length\":500}},\"partII\":[{\"id\":\"VehicleSafetyExtensions\",\"value\":{\"events\":null,\"pathHistory\":{\"initialPosition\":null,\"currGNSSstatus\":null,\"crumbData\":[{\"elevationOffset\":-0.5,\"heading\":null,\"latOffset\":0.0000038,\"lonOffset\":0.0001137,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":31.41},{\"elevationOffset\":-0.4,\"heading\":null,\"latOffset\":0.0000339,\"lonOffset\":0.0001695,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":32.05},{\"elevationOffset\":0,\"heading\":null,\"latOffset\":0.000184,\"lonOffset\":0.0002106,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":36.29},{\"elevationOffset\":0,\"heading\":null,\"latOffset\":0.0003092,\"lonOffset\":0.0003081,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":37.22},{\"elevationOffset\":0.4,\"heading\":null,\"latOffset\":0.0004354,\"lonOffset\":0.0003906,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":38.15},{\"elevationOffset\":1.4,\"heading\":null,\"latOffset\":0.0007727,\"lonOffset\":0.0004391,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":40.39},{\"elevationOffset\":1.4,\"heading\":null,\"latOffset\":0.00084,\"lonOffset\":0.0004778,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":41.61},{\"elevationOffset\":1.4,\"heading\":null,\"latOffset\":0.0008649,\"lonOffset\":0.0005765,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":43.48},{\"elevationOffset\":1.7,\"heading\":null,\"latOffset\":0.0008086,\"lonOffset\":0.0015482,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":68.53},{\"elevationOffset\":1.7,\"heading\":null,\"latOffset\":0.0007738,\"lonOffset\":0.0015944,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":72.09},{\"elevationOffset\":2.1,\"heading\":null,\"latOffset\":0.0007349,\"lonOffset\":0.0015747,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":76.97}]},\"pathPrediction\":{\"confidence\":100,\"radiusOfCurve\":0},\"lights\":null}},{\"id\":\"SupplementalVehicleExtensions\",\"value\":{\"classification\":66,\"classDetails\":{\"fuelType\":null,\"hpmsType\":\"none\",\"iso3883\":null,\"keyType\":66,\"responderType\":null,\"responseEquip\":null,\"role\":null,\"vehicleType\":null},\"vehicleData\":{\"bumpers\":null,\"height\":1.8,\"mass\":2800,\"trailerWeight\":null},\"weatherReport\":{\"friction\":null,\"isRaining\":\"ERROR\",\"precipSituation\":\"UNKNOWN\",\"rainRate\":null,\"roadFriction\":0,\"solarRadiation\":null},\"weatherProbe\":{\"airPressure\":860,\"airTemp\":71,\"rainRates\":null},\"obstacle\":null,\"status\":null,\"speedProfile\":null,\"theRTCM\":null}}]},\"dataType\":\"us.dot.its.jpo.ode.plugin.j2735.J2735Bsm\"}}";
+
+ // Increase Time from Bsm 1
+ String inputBsm3 = "{\"metadata\":{\"bsmSource\":\"RV\",\"logFileName\":\"\",\"recordType\":\"bsmTx\",\"securityResultCode\":\"success\",\"receivedMessageDetails\":{\"locationData\":{\"latitude\":\"\",\"longitude\":\"\",\"elevation\":\"\",\"speed\":\"\",\"heading\":\"\"},\"rxSource\":\"RV\"},\"encodings\":null,\"payloadType\":\"us.dot.its.jpo.ode.model.OdeBsmPayload\",\"serialId\":{\"streamId\":\"f1bfed26-d986-4a0c-b8a4-68b1e5ac1348\",\"bundleSize\":1,\"bundleId\":0,\"recordId\":0,\"serialNumber\":0},\"odeReceivedAt\":\"2024-07-01T15:01:02.797Z\",\"schemaVersion\":6,\"maxDurationTime\":0,\"recordGeneratedAt\":\"\",\"recordGeneratedBy\":null,\"sanitized\":false,\"odePacketID\":\"\",\"odeTimStartDateTime\":\"\",\"originIp\":\"10.164.6.18\"},\"payload\":{\"data\":{\"coreData\":{\"msgCnt\":7,\"id\":\"E79423A3\",\"secMark\":52597,\"position\":{\"latitude\":40.2970849,\"longitude\":-111.6956069,\"elevation\":1439},\"accelSet\":{\"accelLat\":2001,\"accelLong\":0,\"accelVert\":-127,\"accelYaw\":0},\"accuracy\":{\"semiMajor\":2,\"semiMinor\":2,\"orientation\":44.49530799},\"transmission\":\"FORWARDGEARS\",\"speed\":0,\"heading\":24.2,\"angle\":0,\"brakes\":{\"wheelBrakes\":{\"leftFront\":true,\"rightFront\":true,\"unavailable\":false,\"leftRear\":true,\"rightRear\":true},\"traction\":\"on\",\"abs\":\"on\",\"scs\":\"on\",\"brakeBoost\":\"off\",\"auxBrakes\":\"unavailable\"},\"size\":{\"width\":230,\"length\":500}},\"partII\":[{\"id\":\"VehicleSafetyExtensions\",\"value\":{\"events\":null,\"pathHistory\":{\"initialPosition\":null,\"currGNSSstatus\":null,\"crumbData\":[{\"elevationOffset\":-0.5,\"heading\":null,\"latOffset\":0.0000038,\"lonOffset\":0.0001137,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":31.41},{\"elevationOffset\":-0.4,\"heading\":null,\"latOffset\":0.0000339,\"lonOffset\":0.0001695,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":32.05},{\"elevationOffset\":0,\"heading\":null,\"latOffset\":0.000184,\"lonOffset\":0.0002106,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":36.29},{\"elevationOffset\":0,\"heading\":null,\"latOffset\":0.0003092,\"lonOffset\":0.0003081,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":37.22},{\"elevationOffset\":0.4,\"heading\":null,\"latOffset\":0.0004354,\"lonOffset\":0.0003906,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":38.15},{\"elevationOffset\":1.4,\"heading\":null,\"latOffset\":0.0007727,\"lonOffset\":0.0004391,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":40.39},{\"elevationOffset\":1.4,\"heading\":null,\"latOffset\":0.00084,\"lonOffset\":0.0004778,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":41.61},{\"elevationOffset\":1.4,\"heading\":null,\"latOffset\":0.0008649,\"lonOffset\":0.0005765,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":43.48},{\"elevationOffset\":1.7,\"heading\":null,\"latOffset\":0.0008086,\"lonOffset\":0.0015482,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":68.53},{\"elevationOffset\":1.7,\"heading\":null,\"latOffset\":0.0007738,\"lonOffset\":0.0015944,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":72.09},{\"elevationOffset\":2.1,\"heading\":null,\"latOffset\":0.0007349,\"lonOffset\":0.0015747,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":76.97}]},\"pathPrediction\":{\"confidence\":100,\"radiusOfCurve\":0},\"lights\":null}},{\"id\":\"SupplementalVehicleExtensions\",\"value\":{\"classification\":66,\"classDetails\":{\"fuelType\":null,\"hpmsType\":\"none\",\"iso3883\":null,\"keyType\":66,\"responderType\":null,\"responseEquip\":null,\"role\":null,\"vehicleType\":null},\"vehicleData\":{\"bumpers\":null,\"height\":1.8,\"mass\":2800,\"trailerWeight\":null},\"weatherReport\":{\"friction\":null,\"isRaining\":\"ERROR\",\"precipSituation\":\"UNKNOWN\",\"rainRate\":null,\"roadFriction\":0,\"solarRadiation\":null},\"weatherProbe\":{\"airPressure\":860,\"airTemp\":71,\"rainRates\":null},\"obstacle\":null,\"status\":null,\"speedProfile\":null,\"theRTCM\":null}}]},\"dataType\":\"us.dot.its.jpo.ode.plugin.j2735.J2735Bsm\"}}";
+
+ // Vehicle Speed not 0
+ String inputBsm4 = "{\"metadata\":{\"bsmSource\":\"RV\",\"logFileName\":\"\",\"recordType\":\"bsmTx\",\"securityResultCode\":\"success\",\"receivedMessageDetails\":{\"locationData\":{\"latitude\":\"\",\"longitude\":\"\",\"elevation\":\"\",\"speed\":\"\",\"heading\":\"\"},\"rxSource\":\"RV\"},\"encodings\":null,\"payloadType\":\"us.dot.its.jpo.ode.model.OdeBsmPayload\",\"serialId\":{\"streamId\":\"f1bfed26-d986-4a0c-b8a4-68b1e5ac1348\",\"bundleSize\":1,\"bundleId\":0,\"recordId\":0,\"serialNumber\":0},\"odeReceivedAt\":\"2024-07-01T15:01:02.897Z\",\"schemaVersion\":6,\"maxDurationTime\":0,\"recordGeneratedAt\":\"\",\"recordGeneratedBy\":null,\"sanitized\":false,\"odePacketID\":\"\",\"odeTimStartDateTime\":\"\",\"originIp\":\"10.164.6.18\"},\"payload\":{\"data\":{\"coreData\":{\"msgCnt\":7,\"id\":\"E79423A3\",\"secMark\":52597,\"position\":{\"latitude\":40.2970849,\"longitude\":-111.6956069,\"elevation\":1439},\"accelSet\":{\"accelLat\":2001,\"accelLong\":0,\"accelVert\":-127,\"accelYaw\":0},\"accuracy\":{\"semiMajor\":2,\"semiMinor\":2,\"orientation\":44.49530799},\"transmission\":\"FORWARDGEARS\",\"speed\":10,\"heading\":24.2,\"angle\":0,\"brakes\":{\"wheelBrakes\":{\"leftFront\":true,\"rightFront\":true,\"unavailable\":false,\"leftRear\":true,\"rightRear\":true},\"traction\":\"on\",\"abs\":\"on\",\"scs\":\"on\",\"brakeBoost\":\"off\",\"auxBrakes\":\"unavailable\"},\"size\":{\"width\":230,\"length\":500}},\"partII\":[{\"id\":\"VehicleSafetyExtensions\",\"value\":{\"events\":null,\"pathHistory\":{\"initialPosition\":null,\"currGNSSstatus\":null,\"crumbData\":[{\"elevationOffset\":-0.5,\"heading\":null,\"latOffset\":0.0000038,\"lonOffset\":0.0001137,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":31.41},{\"elevationOffset\":-0.4,\"heading\":null,\"latOffset\":0.0000339,\"lonOffset\":0.0001695,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":32.05},{\"elevationOffset\":0,\"heading\":null,\"latOffset\":0.000184,\"lonOffset\":0.0002106,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":36.29},{\"elevationOffset\":0,\"heading\":null,\"latOffset\":0.0003092,\"lonOffset\":0.0003081,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":37.22},{\"elevationOffset\":0.4,\"heading\":null,\"latOffset\":0.0004354,\"lonOffset\":0.0003906,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":38.15},{\"elevationOffset\":1.4,\"heading\":null,\"latOffset\":0.0007727,\"lonOffset\":0.0004391,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":40.39},{\"elevationOffset\":1.4,\"heading\":null,\"latOffset\":0.00084,\"lonOffset\":0.0004778,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":41.61},{\"elevationOffset\":1.4,\"heading\":null,\"latOffset\":0.0008649,\"lonOffset\":0.0005765,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":43.48},{\"elevationOffset\":1.7,\"heading\":null,\"latOffset\":0.0008086,\"lonOffset\":0.0015482,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":68.53},{\"elevationOffset\":1.7,\"heading\":null,\"latOffset\":0.0007738,\"lonOffset\":0.0015944,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":72.09},{\"elevationOffset\":2.1,\"heading\":null,\"latOffset\":0.0007349,\"lonOffset\":0.0015747,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":76.97}]},\"pathPrediction\":{\"confidence\":100,\"radiusOfCurve\":0},\"lights\":null}},{\"id\":\"SupplementalVehicleExtensions\",\"value\":{\"classification\":66,\"classDetails\":{\"fuelType\":null,\"hpmsType\":\"none\",\"iso3883\":null,\"keyType\":66,\"responderType\":null,\"responseEquip\":null,\"role\":null,\"vehicleType\":null},\"vehicleData\":{\"bumpers\":null,\"height\":1.8,\"mass\":2800,\"trailerWeight\":null},\"weatherReport\":{\"friction\":null,\"isRaining\":\"ERROR\",\"precipSituation\":\"UNKNOWN\",\"rainRate\":null,\"roadFriction\":0,\"solarRadiation\":null},\"weatherProbe\":{\"airPressure\":860,\"airTemp\":71,\"rainRates\":null},\"obstacle\":null,\"status\":null,\"speedProfile\":null,\"theRTCM\":null}}]},\"dataType\":\"us.dot.its.jpo.ode.plugin.j2735.J2735Bsm\"}}";
+
+ // Vehicle Position has changed
+ String inputBsm5 = "{\"metadata\":{\"bsmSource\":\"RV\",\"logFileName\":\"\",\"recordType\":\"bsmTx\",\"securityResultCode\":\"success\",\"receivedMessageDetails\":{\"locationData\":{\"latitude\":\"\",\"longitude\":\"\",\"elevation\":\"\",\"speed\":\"\",\"heading\":\"\"},\"rxSource\":\"RV\"},\"encodings\":null,\"payloadType\":\"us.dot.its.jpo.ode.model.OdeBsmPayload\",\"serialId\":{\"streamId\":\"f1bfed26-d986-4a0c-b8a4-68b1e5ac1348\",\"bundleSize\":1,\"bundleId\":0,\"recordId\":0,\"serialNumber\":0},\"odeReceivedAt\":\"2024-07-01T15:01:02.997Z\",\"schemaVersion\":6,\"maxDurationTime\":0,\"recordGeneratedAt\":\"\",\"recordGeneratedBy\":null,\"sanitized\":false,\"odePacketID\":\"\",\"odeTimStartDateTime\":\"\",\"originIp\":\"10.164.6.18\"},\"payload\":{\"data\":{\"coreData\":{\"msgCnt\":7,\"id\":\"E79423A3\",\"secMark\":52597,\"position\":{\"latitude\":40.2970949,\"longitude\":-111.6956169,\"elevation\":1439},\"accelSet\":{\"accelLat\":2001,\"accelLong\":0,\"accelVert\":-127,\"accelYaw\":0},\"accuracy\":{\"semiMajor\":2,\"semiMinor\":2,\"orientation\":44.49530799},\"transmission\":\"FORWARDGEARS\",\"speed\":0,\"heading\":24.2,\"angle\":0,\"brakes\":{\"wheelBrakes\":{\"leftFront\":true,\"rightFront\":true,\"unavailable\":false,\"leftRear\":true,\"rightRear\":true},\"traction\":\"on\",\"abs\":\"on\",\"scs\":\"on\",\"brakeBoost\":\"off\",\"auxBrakes\":\"unavailable\"},\"size\":{\"width\":230,\"length\":500}},\"partII\":[{\"id\":\"VehicleSafetyExtensions\",\"value\":{\"events\":null,\"pathHistory\":{\"initialPosition\":null,\"currGNSSstatus\":null,\"crumbData\":[{\"elevationOffset\":-0.5,\"heading\":null,\"latOffset\":0.0000038,\"lonOffset\":0.0001137,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":31.41},{\"elevationOffset\":-0.4,\"heading\":null,\"latOffset\":0.0000339,\"lonOffset\":0.0001695,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":32.05},{\"elevationOffset\":0,\"heading\":null,\"latOffset\":0.000184,\"lonOffset\":0.0002106,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":36.29},{\"elevationOffset\":0,\"heading\":null,\"latOffset\":0.0003092,\"lonOffset\":0.0003081,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":37.22},{\"elevationOffset\":0.4,\"heading\":null,\"latOffset\":0.0004354,\"lonOffset\":0.0003906,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":38.15},{\"elevationOffset\":1.4,\"heading\":null,\"latOffset\":0.0007727,\"lonOffset\":0.0004391,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":40.39},{\"elevationOffset\":1.4,\"heading\":null,\"latOffset\":0.00084,\"lonOffset\":0.0004778,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":41.61},{\"elevationOffset\":1.4,\"heading\":null,\"latOffset\":0.0008649,\"lonOffset\":0.0005765,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":43.48},{\"elevationOffset\":1.7,\"heading\":null,\"latOffset\":0.0008086,\"lonOffset\":0.0015482,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":68.53},{\"elevationOffset\":1.7,\"heading\":null,\"latOffset\":0.0007738,\"lonOffset\":0.0015944,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":72.09},{\"elevationOffset\":2.1,\"heading\":null,\"latOffset\":0.0007349,\"lonOffset\":0.0015747,\"posAccuracy\":null,\"speed\":null,\"timeOffset\":76.97}]},\"pathPrediction\":{\"confidence\":100,\"radiusOfCurve\":0},\"lights\":null}},{\"id\":\"SupplementalVehicleExtensions\",\"value\":{\"classification\":66,\"classDetails\":{\"fuelType\":null,\"hpmsType\":\"none\",\"iso3883\":null,\"keyType\":66,\"responderType\":null,\"responseEquip\":null,\"role\":null,\"vehicleType\":null},\"vehicleData\":{\"bumpers\":null,\"height\":1.8,\"mass\":2800,\"trailerWeight\":null},\"weatherReport\":{\"friction\":null,\"isRaining\":\"ERROR\",\"precipSituation\":\"UNKNOWN\",\"rainRate\":null,\"roadFriction\":0,\"solarRadiation\":null},\"weatherProbe\":{\"airPressure\":860,\"airTemp\":71,\"rainRates\":null},\"obstacle\":null,\"status\":null,\"speedProfile\":null,\"theRTCM\":null}}]},\"dataType\":\"us.dot.its.jpo.ode.plugin.j2735.J2735Bsm\"}}";
+
+
+ @Autowired
+ DeduplicatorProperties props;
+
+
+
+ @Test
+ public void testTopology() {
+
+
+
+
+
+ props = new DeduplicatorProperties();
+ props.setAlwaysIncludeAtSpeed(1);
+ props.setenableOdeBsmDeduplication(true);
+ props.setMaximumPositionDelta(1);
+ props.setMaximumTimeDelta(10000);
+ props.setkafkaTopicOdeBsmJson(inputTopic);
+ props.setkafkaTopicDeduplicatedOdeBsmJson(outputTopic);
+
+ BsmDeduplicatorTopology bsmDeduplicatorTopology = new BsmDeduplicatorTopology(props);
+ Topology topology = bsmDeduplicatorTopology.buildTopology();
+
+ try (TopologyTestDriver driver = new TopologyTestDriver(topology)) {
+
+
+ TestInputTopic inputOdeBsmData = driver.createInputTopic(
+ inputTopic,
+ Serdes.Void().serializer(),
+ Serdes.String().serializer());
+
+
+ TestOutputTopic outputOdeBsmData = driver.createOutputTopic(
+ outputTopic,
+ Serdes.String().deserializer(),
+ JsonSerdes.OdeBsm().deserializer());
+
+ inputOdeBsmData.pipeInput(null, inputBsm1);
+ inputOdeBsmData.pipeInput(null, inputBsm2);
+ inputOdeBsmData.pipeInput(null, inputBsm3);
+ inputOdeBsmData.pipeInput(null, inputBsm4);
+ inputOdeBsmData.pipeInput(null, inputBsm5);
+
+ List> bsmDeduplicationResults = outputOdeBsmData.readKeyValuesToList();
+
+ // validate that only 3 messages make it through
+ assertEquals(4, bsmDeduplicationResults.size());
+
+ objectMapper = new ObjectMapper();
+ OdeBsmData bsm1 = objectMapper.readValue(inputBsm1, OdeBsmData.class);
+ OdeBsmData bsm3 = objectMapper.readValue(inputBsm3, OdeBsmData.class);
+ OdeBsmData bsm4 = objectMapper.readValue(inputBsm4, OdeBsmData.class);
+ OdeBsmData bsm5 = objectMapper.readValue(inputBsm5, OdeBsmData.class);
+
+
+ assertEquals(bsm1.getMetadata().getOdeReceivedAt(), bsmDeduplicationResults.get(0).value.getMetadata().getOdeReceivedAt());
+ assertEquals(bsm3.getMetadata().getOdeReceivedAt(), bsmDeduplicationResults.get(1).value.getMetadata().getOdeReceivedAt());
+ assertEquals(bsm4.getMetadata().getOdeReceivedAt(), bsmDeduplicationResults.get(2).value.getMetadata().getOdeReceivedAt());
+ assertEquals(bsm5.getMetadata().getOdeReceivedAt(), bsmDeduplicationResults.get(3).value.getMetadata().getOdeReceivedAt());
+
+ } catch (JsonMappingException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (JsonProcessingException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+}
\ No newline at end of file
diff --git a/jpo-deduplicator/jpo-deduplicator/src/test/java/deduplicator/MapDeduplicatorTopologyTest.java b/jpo-deduplicator/jpo-deduplicator/src/test/java/deduplicator/MapDeduplicatorTopologyTest.java
new file mode 100644
index 0000000..a339677
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/test/java/deduplicator/MapDeduplicatorTopologyTest.java
@@ -0,0 +1,96 @@
+package deduplicator;
+
+
+
+import org.apache.kafka.common.serialization.Serdes;
+import org.apache.kafka.streams.KeyValue;
+import org.apache.kafka.streams.TestInputTopic;
+import org.apache.kafka.streams.TestOutputTopic;
+import org.apache.kafka.streams.Topology;
+import org.apache.kafka.streams.TopologyTestDriver;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import us.dot.its.jpo.deduplicator.DeduplicatorProperties;
+import us.dot.its.jpo.deduplicator.deduplicator.topologies.MapDeduplicatorTopology;
+import us.dot.its.jpo.geojsonconverter.serialization.JsonSerdes;
+import us.dot.its.jpo.ode.model.OdeMapData;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.List;
+
+
+public class MapDeduplicatorTopologyTest {
+
+ String inputTopic = "topic.OdeMapJson";
+ String outputTopic = "topic.DeduplicatedOdeMapJson";
+ ObjectMapper objectMapper;
+
+ String inputMap1 = "{\"metadata\":{\"logFileName\":\"\",\"recordType\":\"mapTx\",\"securityResultCode\":\"success\",\"receivedMessageDetails\":{\"locationData\":null,\"rxSource\":\"NA\"},\"encodings\":null,\"payloadType\":\"us.dot.its.jpo.ode.model.OdeMapPayload\",\"serialId\":{\"streamId\":\"139ba3af-e501-4854-9327-b9e1a2ed8dfb\",\"bundleSize\":1,\"bundleId\":0,\"recordId\":0,\"serialNumber\":0},\"odeReceivedAt\":\"2024-02-22T21:12:44.308Z\",\"schemaVersion\":6,\"maxDurationTime\":0,\"recordGeneratedAt\":\"\",\"recordGeneratedBy\":null,\"sanitized\":false,\"odePacketID\":\"\",\"odeTimStartDateTime\":\"\",\"mapSource\":\"RSU\",\"originIp\":\"10.11.81.12\"},\"payload\":{\"data\":{\"timeStamp\":null,\"msgIssueRevision\":2,\"layerType\":\"intersectionData\",\"layerID\":0,\"intersections\":{\"intersectionGeometry\":[{\"name\":null,\"id\":{\"region\":null,\"id\":12109},\"revision\":2,\"refPoint\":{\"latitude\":39.5880413,\"longitude\":-105.0908854,\"elevation\":1691},\"laneWidth\":366,\"speedLimits\":null,\"laneSet\":{\"GenericLane\":[{\"laneID\":1,\"name\":null,\"ingressApproach\":1,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":1511,\"y\":-1514},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":723,\"y\":-3116},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":892,\"y\":-3818},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":702,\"y\":-2507},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":681,\"y\":-2412},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":827,\"y\":-2798},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":1176,\"y\":-3614},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":683,\"y\":-1992},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":1000,\"y\":-2818},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":466,\"y\":-1295},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":{\"x\":2505,\"y\":-6164},\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":{\"x\":6037,\"y\":-13380},\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":70}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":15,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":2,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":2,\"name\":null,\"ingressApproach\":1,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":1192,\"y\":-1619},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":807,\"y\":-3733},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":{\"x\":1010,\"y\":-4226},\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":{\"x\":1612,\"y\":-5477},\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":1142,\"y\":-3648},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":1131,\"y\":-3343},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":{\"x\":2259,\"y\":-6170},\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":{\"x\":2359,\"y\":-5737},\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":{\"x\":5300,\"y\":-11774},\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":50}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":14,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":2,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":3,\"name\":null,\"ingressApproach\":1,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":805,\"y\":-1704},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":380,\"y\":-1813},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":832,\"y\":-3451},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":{\"x\":202,\"y\":-872},\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":{\"x\":340,\"y\":-482},\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-10}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":10,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":2,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":6,\"name\":null,\"ingressApproach\":null,\"egressApproach\":2,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":false,\"egressPath\":true},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":null,\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-988,\"y\":-2151},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":20}},{\"delta\":{\"nodeXY1\":{\"x\":69,\"y\":-329},\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null}]}},\"connectsTo\":null,\"overlays\":null},{\"laneID\":5,\"name\":null,\"ingressApproach\":null,\"egressApproach\":2,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":false,\"egressPath\":true},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":null,\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-630,\"y\":-2062},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":{\"x\":76,\"y\":-377},\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}}]}},\"connectsTo\":null,\"overlays\":null},{\"laneID\":4,\"name\":null,\"ingressApproach\":null,\"egressApproach\":2,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":false,\"egressPath\":true},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":null,\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-245,\"y\":-2001},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":{\"x\":76,\"y\":-349},\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null}]}},\"connectsTo\":null,\"overlays\":null},{\"laneID\":10,\"name\":null,\"ingressApproach\":null,\"egressApproach\":4,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":false,\"egressPath\":true},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":null,\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2374,\"y\":232},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":{\"x\":-357,\"y\":-96},\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null}]}},\"connectsTo\":null,\"overlays\":null},{\"laneID\":8,\"name\":null,\"ingressApproach\":3,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2246,\"y\":-514},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2644,\"y\":-581},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2887,\"y\":-442},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-3583,\"y\":-339},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-3757,\"y\":27},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-3249,\"y\":361},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2050,\"y\":478},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-3893,\"y\":1184},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2625,\"y\":766},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2524,\"y\":607},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2280,\"y\":325},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":15,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":4,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":7,\"name\":null,\"ingressApproach\":3,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2216,\"y\":-915},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2322,\"y\":-471},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-1877,\"y\":-349},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-1787,\"y\":-235},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2666,\"y\":-102},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":6,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":null,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":9,\"name\":null,\"ingressApproach\":3,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2295,\"y\":-169},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2420,\"y\":-499},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-1968,\"y\":-339},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-1843,\"y\":-256},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2121,\"y\":-339},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":14,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":4,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":12,\"name\":null,\"ingressApproach\":5,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-1364,\"y\":1705},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":{\"x\":-885,\"y\":4854},\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-410,\"y\":2559},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-333,\"y\":1847},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-319,\"y\":3244},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":{\"x\":-500,\"y\":4510},\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-333,\"y\":3403},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-268,\"y\":2982},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":{\"x\":-758,\"y\":11212},\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":{\"x\":-323,\"y\":11176},\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-70}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":5,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":6,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":13,\"name\":null,\"ingressApproach\":5,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-992,\"y\":1735},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":{\"x\":-896,\"y\":4816},\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-326,\"y\":2227},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-333,\"y\":2366},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-410,\"y\":3030},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-326,\"y\":3009},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-271,\"y\":2518},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-264,\"y\":2857},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-243,\"y\":2504},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":{\"x\":-740,\"y\":11165},\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":{\"x\":-253,\"y\":11359},\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-70}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":4,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":6,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":11,\"name\":null,\"ingressApproach\":5,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-1744,\"y\":1607},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-563,\"y\":3136},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-352,\"y\":2336},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-223,\"y\":1407},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-155,\"y\":1778},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":10,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":null,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":14,\"name\":null,\"ingressApproach\":null,\"egressApproach\":6,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":false,\"egressPath\":true},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":null,\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":398,\"y\":1931},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-10}},{\"delta\":{\"nodeXY1\":{\"x\":-76,\"y\":356},\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null}]}},\"connectsTo\":null,\"overlays\":null},{\"laneID\":15,\"name\":null,\"ingressApproach\":null,\"egressApproach\":6,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":false,\"egressPath\":true},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":null,\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":838,\"y\":1985},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-20}},{\"delta\":{\"nodeXY1\":{\"x\":-89,\"y\":349},\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null}]}},\"connectsTo\":null,\"overlays\":null}]}}]},\"roadSegments\":null,\"dataParameters\":null,\"restrictionList\":null},\"dataType\":\"us.dot.its.jpo.ode.plugin.j2735.J2735MAP\"}}";
+ String inputMap2 = "{\"metadata\":{\"logFileName\":\"\",\"recordType\":\"mapTx\",\"securityResultCode\":\"success\",\"receivedMessageDetails\":{\"locationData\":null,\"rxSource\":\"NA\"},\"encodings\":null,\"payloadType\":\"us.dot.its.jpo.ode.model.OdeMapPayload\",\"serialId\":{\"streamId\":\"139ba3af-e501-4854-9327-b9e1a2ed8dfb\",\"bundleSize\":1,\"bundleId\":0,\"recordId\":0,\"serialNumber\":0},\"odeReceivedAt\":\"2024-02-22T21:12:45.322Z\",\"schemaVersion\":6,\"maxDurationTime\":0,\"recordGeneratedAt\":\"\",\"recordGeneratedBy\":null,\"sanitized\":false,\"odePacketID\":\"\",\"odeTimStartDateTime\":\"\",\"mapSource\":\"RSU\",\"originIp\":\"10.11.81.12\"},\"payload\":{\"data\":{\"timeStamp\":null,\"msgIssueRevision\":2,\"layerType\":\"intersectionData\",\"layerID\":0,\"intersections\":{\"intersectionGeometry\":[{\"name\":null,\"id\":{\"region\":null,\"id\":12109},\"revision\":2,\"refPoint\":{\"latitude\":39.5880413,\"longitude\":-105.0908854,\"elevation\":1691},\"laneWidth\":366,\"speedLimits\":null,\"laneSet\":{\"GenericLane\":[{\"laneID\":1,\"name\":null,\"ingressApproach\":1,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":1511,\"y\":-1514},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":723,\"y\":-3116},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":892,\"y\":-3818},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":702,\"y\":-2507},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":681,\"y\":-2412},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":827,\"y\":-2798},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":1176,\"y\":-3614},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":683,\"y\":-1992},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":1000,\"y\":-2818},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":466,\"y\":-1295},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":{\"x\":2505,\"y\":-6164},\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":{\"x\":6037,\"y\":-13380},\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":70}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":15,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":2,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":2,\"name\":null,\"ingressApproach\":1,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":1192,\"y\":-1619},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":807,\"y\":-3733},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":{\"x\":1010,\"y\":-4226},\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":{\"x\":1612,\"y\":-5477},\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":1142,\"y\":-3648},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":1131,\"y\":-3343},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":{\"x\":2259,\"y\":-6170},\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":{\"x\":2359,\"y\":-5737},\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":{\"x\":5300,\"y\":-11774},\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":50}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":14,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":2,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":3,\"name\":null,\"ingressApproach\":1,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":805,\"y\":-1704},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":380,\"y\":-1813},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":832,\"y\":-3451},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":{\"x\":202,\"y\":-872},\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":{\"x\":340,\"y\":-482},\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-10}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":10,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":2,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":6,\"name\":null,\"ingressApproach\":null,\"egressApproach\":2,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":false,\"egressPath\":true},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":null,\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-988,\"y\":-2151},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":20}},{\"delta\":{\"nodeXY1\":{\"x\":69,\"y\":-329},\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null}]}},\"connectsTo\":null,\"overlays\":null},{\"laneID\":5,\"name\":null,\"ingressApproach\":null,\"egressApproach\":2,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":false,\"egressPath\":true},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":null,\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-630,\"y\":-2062},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":{\"x\":76,\"y\":-377},\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}}]}},\"connectsTo\":null,\"overlays\":null},{\"laneID\":4,\"name\":null,\"ingressApproach\":null,\"egressApproach\":2,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":false,\"egressPath\":true},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":null,\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-245,\"y\":-2001},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":{\"x\":76,\"y\":-349},\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null}]}},\"connectsTo\":null,\"overlays\":null},{\"laneID\":10,\"name\":null,\"ingressApproach\":null,\"egressApproach\":4,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":false,\"egressPath\":true},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":null,\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2374,\"y\":232},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":{\"x\":-357,\"y\":-96},\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null}]}},\"connectsTo\":null,\"overlays\":null},{\"laneID\":8,\"name\":null,\"ingressApproach\":3,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2246,\"y\":-514},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2644,\"y\":-581},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2887,\"y\":-442},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-3583,\"y\":-339},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-3757,\"y\":27},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-3249,\"y\":361},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2050,\"y\":478},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-3893,\"y\":1184},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2625,\"y\":766},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2524,\"y\":607},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2280,\"y\":325},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":15,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":4,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":7,\"name\":null,\"ingressApproach\":3,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2216,\"y\":-915},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2322,\"y\":-471},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-1877,\"y\":-349},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-1787,\"y\":-235},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2666,\"y\":-102},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":6,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":null,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":9,\"name\":null,\"ingressApproach\":3,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2295,\"y\":-169},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2420,\"y\":-499},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-1968,\"y\":-339},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-1843,\"y\":-256},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2121,\"y\":-339},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":14,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":4,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":12,\"name\":null,\"ingressApproach\":5,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-1364,\"y\":1705},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":{\"x\":-885,\"y\":4854},\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-410,\"y\":2559},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-333,\"y\":1847},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-319,\"y\":3244},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":{\"x\":-500,\"y\":4510},\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-333,\"y\":3403},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-268,\"y\":2982},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":{\"x\":-758,\"y\":11212},\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":{\"x\":-323,\"y\":11176},\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-70}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":5,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":6,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":13,\"name\":null,\"ingressApproach\":5,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-992,\"y\":1735},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":{\"x\":-896,\"y\":4816},\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-326,\"y\":2227},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-333,\"y\":2366},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-410,\"y\":3030},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-326,\"y\":3009},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-271,\"y\":2518},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-264,\"y\":2857},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-243,\"y\":2504},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":{\"x\":-740,\"y\":11165},\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":{\"x\":-253,\"y\":11359},\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-70}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":4,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":6,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":11,\"name\":null,\"ingressApproach\":5,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-1744,\"y\":1607},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-563,\"y\":3136},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-352,\"y\":2336},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-223,\"y\":1407},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-155,\"y\":1778},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":10,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":null,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":14,\"name\":null,\"ingressApproach\":null,\"egressApproach\":6,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":false,\"egressPath\":true},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":null,\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":398,\"y\":1931},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-10}},{\"delta\":{\"nodeXY1\":{\"x\":-76,\"y\":356},\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null}]}},\"connectsTo\":null,\"overlays\":null},{\"laneID\":15,\"name\":null,\"ingressApproach\":null,\"egressApproach\":6,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":false,\"egressPath\":true},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":null,\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":838,\"y\":1985},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-20}},{\"delta\":{\"nodeXY1\":{\"x\":-89,\"y\":349},\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null}]}},\"connectsTo\":null,\"overlays\":null}]}}]},\"roadSegments\":null,\"dataParameters\":null,\"restrictionList\":null},\"dataType\":\"us.dot.its.jpo.ode.plugin.j2735.J2735MAP\"}}";
+ String inputMap3 = "{\"metadata\":{\"logFileName\":\"\",\"recordType\":\"mapTx\",\"securityResultCode\":\"success\",\"receivedMessageDetails\":{\"locationData\":null,\"rxSource\":\"NA\"},\"encodings\":null,\"payloadType\":\"us.dot.its.jpo.ode.model.OdeMapPayload\",\"serialId\":{\"streamId\":\"139ba3af-e501-4854-9327-b9e1a2ed8dfb\",\"bundleSize\":1,\"bundleId\":0,\"recordId\":0,\"serialNumber\":0},\"odeReceivedAt\":\"2024-02-22T21:12:48.355Z\",\"schemaVersion\":6,\"maxDurationTime\":0,\"recordGeneratedAt\":\"\",\"recordGeneratedBy\":null,\"sanitized\":false,\"odePacketID\":\"\",\"odeTimStartDateTime\":\"\",\"mapSource\":\"RSU\",\"originIp\":\"10.11.81.12\"},\"payload\":{\"data\":{\"timeStamp\":null,\"msgIssueRevision\":2,\"layerType\":\"intersectionData\",\"layerID\":0,\"intersections\":{\"intersectionGeometry\":[{\"name\":null,\"id\":{\"region\":null,\"id\":12109},\"revision\":2,\"refPoint\":{\"latitude\":39.5880413,\"longitude\":-105.0908854,\"elevation\":1500},\"laneWidth\":366,\"speedLimits\":null,\"laneSet\":{\"GenericLane\":[{\"laneID\":1,\"name\":null,\"ingressApproach\":1,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":1511,\"y\":-1514},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":723,\"y\":-3116},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":892,\"y\":-3818},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":702,\"y\":-2507},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":681,\"y\":-2412},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":827,\"y\":-2798},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":1176,\"y\":-3614},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":683,\"y\":-1992},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":1000,\"y\":-2818},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":466,\"y\":-1295},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":{\"x\":2505,\"y\":-6164},\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":{\"x\":6037,\"y\":-13380},\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":70}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":15,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":2,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":2,\"name\":null,\"ingressApproach\":1,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":1192,\"y\":-1619},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":807,\"y\":-3733},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":{\"x\":1010,\"y\":-4226},\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":{\"x\":1612,\"y\":-5477},\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":1142,\"y\":-3648},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":1131,\"y\":-3343},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":{\"x\":2259,\"y\":-6170},\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":{\"x\":2359,\"y\":-5737},\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":{\"x\":5300,\"y\":-11774},\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":50}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":14,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":2,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":3,\"name\":null,\"ingressApproach\":1,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":805,\"y\":-1704},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":380,\"y\":-1813},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":832,\"y\":-3451},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":{\"x\":202,\"y\":-872},\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":{\"x\":340,\"y\":-482},\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-10}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":10,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":2,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":6,\"name\":null,\"ingressApproach\":null,\"egressApproach\":2,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":false,\"egressPath\":true},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":null,\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-988,\"y\":-2151},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":20}},{\"delta\":{\"nodeXY1\":{\"x\":69,\"y\":-329},\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null}]}},\"connectsTo\":null,\"overlays\":null},{\"laneID\":5,\"name\":null,\"ingressApproach\":null,\"egressApproach\":2,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":false,\"egressPath\":true},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":null,\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-630,\"y\":-2062},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":{\"x\":76,\"y\":-377},\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}}]}},\"connectsTo\":null,\"overlays\":null},{\"laneID\":4,\"name\":null,\"ingressApproach\":null,\"egressApproach\":2,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":false,\"egressPath\":true},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":null,\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-245,\"y\":-2001},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":{\"x\":76,\"y\":-349},\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null}]}},\"connectsTo\":null,\"overlays\":null},{\"laneID\":10,\"name\":null,\"ingressApproach\":null,\"egressApproach\":4,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":false,\"egressPath\":true},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":null,\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2374,\"y\":232},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":{\"x\":-357,\"y\":-96},\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null}]}},\"connectsTo\":null,\"overlays\":null},{\"laneID\":8,\"name\":null,\"ingressApproach\":3,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2246,\"y\":-514},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2644,\"y\":-581},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2887,\"y\":-442},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-3583,\"y\":-339},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-3757,\"y\":27},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-3249,\"y\":361},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2050,\"y\":478},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-3893,\"y\":1184},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2625,\"y\":766},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2524,\"y\":607},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2280,\"y\":325},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":15,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":4,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":7,\"name\":null,\"ingressApproach\":3,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2216,\"y\":-915},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2322,\"y\":-471},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-1877,\"y\":-349},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-1787,\"y\":-235},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2666,\"y\":-102},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":6,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":null,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":9,\"name\":null,\"ingressApproach\":3,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2295,\"y\":-169},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2420,\"y\":-499},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-1968,\"y\":-339},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-1843,\"y\":-256},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2121,\"y\":-339},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":14,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":4,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":12,\"name\":null,\"ingressApproach\":5,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-1364,\"y\":1705},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":{\"x\":-885,\"y\":4854},\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-410,\"y\":2559},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-333,\"y\":1847},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-319,\"y\":3244},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":{\"x\":-500,\"y\":4510},\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-333,\"y\":3403},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-268,\"y\":2982},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":{\"x\":-758,\"y\":11212},\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":{\"x\":-323,\"y\":11176},\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-70}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":5,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":6,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":13,\"name\":null,\"ingressApproach\":5,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-992,\"y\":1735},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":{\"x\":-896,\"y\":4816},\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-326,\"y\":2227},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-333,\"y\":2366},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-410,\"y\":3030},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-326,\"y\":3009},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-271,\"y\":2518},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-264,\"y\":2857},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-243,\"y\":2504},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":{\"x\":-740,\"y\":11165},\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":{\"x\":-253,\"y\":11359},\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-70}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":4,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":6,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":11,\"name\":null,\"ingressApproach\":5,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-1744,\"y\":1607},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-563,\"y\":3136},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-352,\"y\":2336},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-223,\"y\":1407},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-155,\"y\":1778},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":10,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":null,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":14,\"name\":null,\"ingressApproach\":null,\"egressApproach\":6,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":false,\"egressPath\":true},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":null,\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":398,\"y\":1931},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-10}},{\"delta\":{\"nodeXY1\":{\"x\":-76,\"y\":356},\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null}]}},\"connectsTo\":null,\"overlays\":null},{\"laneID\":15,\"name\":null,\"ingressApproach\":null,\"egressApproach\":6,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":false,\"egressPath\":true},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":null,\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":838,\"y\":1985},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-20}},{\"delta\":{\"nodeXY1\":{\"x\":-89,\"y\":349},\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null}]}},\"connectsTo\":null,\"overlays\":null}]}}]},\"roadSegments\":null,\"dataParameters\":null,\"restrictionList\":null},\"dataType\":\"us.dot.its.jpo.ode.plugin.j2735.J2735MAP\"}}";
+ String inputMap4 = "{\"metadata\":{\"logFileName\":\"\",\"recordType\":\"mapTx\",\"securityResultCode\":\"success\",\"receivedMessageDetails\":{\"locationData\":null,\"rxSource\":\"NA\"},\"encodings\":null,\"payloadType\":\"us.dot.its.jpo.ode.model.OdeMapPayload\",\"serialId\":{\"streamId\":\"139ba3af-e501-4854-9327-b9e1a2ed8dfb\",\"bundleSize\":1,\"bundleId\":0,\"recordId\":0,\"serialNumber\":0},\"odeReceivedAt\":\"2024-02-22T22:12:49.362Z\",\"schemaVersion\":6,\"maxDurationTime\":0,\"recordGeneratedAt\":\"\",\"recordGeneratedBy\":null,\"sanitized\":false,\"odePacketID\":\"\",\"odeTimStartDateTime\":\"\",\"mapSource\":\"RSU\",\"originIp\":\"10.11.81.12\"},\"payload\":{\"data\":{\"timeStamp\":null,\"msgIssueRevision\":2,\"layerType\":\"intersectionData\",\"layerID\":0,\"intersections\":{\"intersectionGeometry\":[{\"name\":null,\"id\":{\"region\":null,\"id\":12109},\"revision\":2,\"refPoint\":{\"latitude\":39.5880413,\"longitude\":-105.0908854,\"elevation\":1500},\"laneWidth\":366,\"speedLimits\":null,\"laneSet\":{\"GenericLane\":[{\"laneID\":1,\"name\":null,\"ingressApproach\":1,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":1511,\"y\":-1514},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":723,\"y\":-3116},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":892,\"y\":-3818},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":702,\"y\":-2507},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":681,\"y\":-2412},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":827,\"y\":-2798},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":1176,\"y\":-3614},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":683,\"y\":-1992},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":1000,\"y\":-2818},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":466,\"y\":-1295},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":{\"x\":2505,\"y\":-6164},\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":{\"x\":6037,\"y\":-13380},\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":70}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":15,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":2,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":2,\"name\":null,\"ingressApproach\":1,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":1192,\"y\":-1619},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":807,\"y\":-3733},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":{\"x\":1010,\"y\":-4226},\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":{\"x\":1612,\"y\":-5477},\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":1142,\"y\":-3648},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":1131,\"y\":-3343},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":{\"x\":2259,\"y\":-6170},\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":{\"x\":2359,\"y\":-5737},\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":{\"x\":5300,\"y\":-11774},\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":50}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":14,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":2,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":3,\"name\":null,\"ingressApproach\":1,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":805,\"y\":-1704},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":380,\"y\":-1813},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":832,\"y\":-3451},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":{\"x\":202,\"y\":-872},\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":{\"x\":340,\"y\":-482},\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-10}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":10,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":2,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":6,\"name\":null,\"ingressApproach\":null,\"egressApproach\":2,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":false,\"egressPath\":true},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":null,\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-988,\"y\":-2151},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":20}},{\"delta\":{\"nodeXY1\":{\"x\":69,\"y\":-329},\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null}]}},\"connectsTo\":null,\"overlays\":null},{\"laneID\":5,\"name\":null,\"ingressApproach\":null,\"egressApproach\":2,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":false,\"egressPath\":true},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":null,\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-630,\"y\":-2062},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":{\"x\":76,\"y\":-377},\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}}]}},\"connectsTo\":null,\"overlays\":null},{\"laneID\":4,\"name\":null,\"ingressApproach\":null,\"egressApproach\":2,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":false,\"egressPath\":true},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":null,\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-245,\"y\":-2001},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":{\"x\":76,\"y\":-349},\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null}]}},\"connectsTo\":null,\"overlays\":null},{\"laneID\":10,\"name\":null,\"ingressApproach\":null,\"egressApproach\":4,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":false,\"egressPath\":true},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":null,\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2374,\"y\":232},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":{\"x\":-357,\"y\":-96},\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null}]}},\"connectsTo\":null,\"overlays\":null},{\"laneID\":8,\"name\":null,\"ingressApproach\":3,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2246,\"y\":-514},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2644,\"y\":-581},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2887,\"y\":-442},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-3583,\"y\":-339},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-3757,\"y\":27},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-3249,\"y\":361},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2050,\"y\":478},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-3893,\"y\":1184},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2625,\"y\":766},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2524,\"y\":607},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2280,\"y\":325},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":15,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":4,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":7,\"name\":null,\"ingressApproach\":3,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2216,\"y\":-915},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2322,\"y\":-471},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-1877,\"y\":-349},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-1787,\"y\":-235},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2666,\"y\":-102},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":6,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":null,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":9,\"name\":null,\"ingressApproach\":3,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2295,\"y\":-169},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2420,\"y\":-499},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-1968,\"y\":-339},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-1843,\"y\":-256},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-2121,\"y\":-339},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":14,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":4,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":12,\"name\":null,\"ingressApproach\":5,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-1364,\"y\":1705},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":{\"x\":-885,\"y\":4854},\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-410,\"y\":2559},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-333,\"y\":1847},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-319,\"y\":3244},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":{\"x\":-500,\"y\":4510},\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-333,\"y\":3403},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-268,\"y\":2982},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":{\"x\":-758,\"y\":11212},\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":{\"x\":-323,\"y\":11176},\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-70}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":5,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":6,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":13,\"name\":null,\"ingressApproach\":5,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-992,\"y\":1735},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":{\"x\":-896,\"y\":4816},\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-326,\"y\":2227},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-333,\"y\":2366},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-410,\"y\":3030},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-326,\"y\":3009},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-271,\"y\":2518},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-264,\"y\":2857},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-243,\"y\":2504},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":{\"x\":-740,\"y\":11165},\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-30}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":{\"x\":-253,\"y\":11359},\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-70}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":4,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":6,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":11,\"name\":null,\"ingressApproach\":5,\"egressApproach\":null,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":true,\"egressPath\":false},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-1744,\"y\":1607},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-563,\"y\":3136},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-20}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":{\"x\":-352,\"y\":2336},\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-223,\"y\":1407},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":10}},{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":-155,\"y\":1778},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":10,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"remoteIntersection\":null,\"signalGroup\":null,\"userClass\":null,\"connectionID\":1}]},\"overlays\":null},{\"laneID\":14,\"name\":null,\"ingressApproach\":null,\"egressApproach\":6,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":false,\"egressPath\":true},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":null,\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":398,\"y\":1931},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-10}},{\"delta\":{\"nodeXY1\":{\"x\":-76,\"y\":356},\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null}]}},\"connectsTo\":null,\"overlays\":null},{\"laneID\":15,\"name\":null,\"ingressApproach\":null,\"egressApproach\":6,\"laneAttributes\":{\"directionalUse\":{\"ingressPath\":false,\"egressPath\":true},\"shareWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false},\"crosswalk\":null,\"bikeLane\":null,\"sidewalk\":null,\"median\":null,\"striping\":null,\"trackedVehicle\":null,\"parking\":null}},\"maneuvers\":null,\"nodeList\":{\"computed\":null,\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY1\":null,\"nodeXY2\":null,\"nodeXY3\":{\"x\":838,\"y\":1985},\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":{\"localNode\":null,\"disabled\":null,\"enabled\":null,\"data\":null,\"dWidth\":null,\"dElevation\":-20}},{\"delta\":{\"nodeXY1\":{\"x\":-89,\"y\":349},\"nodeXY2\":null,\"nodeXY3\":null,\"nodeXY4\":null,\"nodeXY5\":null,\"nodeXY6\":null,\"nodeLatLon\":null},\"attributes\":null}]}},\"connectsTo\":null,\"overlays\":null}]}}]},\"roadSegments\":null,\"dataParameters\":null,\"restrictionList\":null},\"dataType\":\"us.dot.its.jpo.ode.plugin.j2735.J2735MAP\"}}";
+
+ @Autowired
+ DeduplicatorProperties props;
+
+
+
+ @Test
+ public void testTopology() {
+
+ props = new DeduplicatorProperties();
+ props.setKafkaTopicOdeMapJson(inputTopic);
+ props.setKafkaTopicDeduplicatedOdeMapJson(outputTopic);
+
+ MapDeduplicatorTopology mapDeduplicatorTopology = new MapDeduplicatorTopology(props, null);
+
+ Topology topology = mapDeduplicatorTopology.buildTopology();
+
+ try (TopologyTestDriver driver = new TopologyTestDriver(topology)) {
+
+
+ TestInputTopic inputOdeMapData = driver.createInputTopic(
+ inputTopic,
+ Serdes.Void().serializer(),
+ Serdes.String().serializer());
+
+
+ TestOutputTopic outputOdeMapData = driver.createOutputTopic(
+ outputTopic,
+ Serdes.String().deserializer(),
+ JsonSerdes.OdeMap().deserializer());
+
+ inputOdeMapData.pipeInput(null, inputMap1);
+ inputOdeMapData.pipeInput(null, inputMap2);
+ inputOdeMapData.pipeInput(null, inputMap3);
+ inputOdeMapData.pipeInput(null, inputMap4);
+
+ List> mapDeduplicationResults = outputOdeMapData.readKeyValuesToList();
+
+ // validate that only 3 messages make it through
+ assertEquals(3, mapDeduplicationResults.size());
+
+ objectMapper = new ObjectMapper();
+ OdeMapData map1 = objectMapper.readValue(inputMap1, OdeMapData.class);
+ OdeMapData map3 = objectMapper.readValue(inputMap3, OdeMapData.class);
+ OdeMapData map4 = objectMapper.readValue(inputMap4, OdeMapData.class);
+
+
+ assertEquals(map1.getMetadata().getOdeReceivedAt(), mapDeduplicationResults.get(0).value.getMetadata().getOdeReceivedAt());
+ assertEquals(map3.getMetadata().getOdeReceivedAt(), mapDeduplicationResults.get(1).value.getMetadata().getOdeReceivedAt());
+ assertEquals(map4.getMetadata().getOdeReceivedAt(), mapDeduplicationResults.get(2).value.getMetadata().getOdeReceivedAt());
+
+ } catch (JsonMappingException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (JsonProcessingException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+}
\ No newline at end of file
diff --git a/jpo-deduplicator/jpo-deduplicator/src/test/java/deduplicator/OdeRawEncodedTimDeduplicatorTopologyTest.java b/jpo-deduplicator/jpo-deduplicator/src/test/java/deduplicator/OdeRawEncodedTimDeduplicatorTopologyTest.java
new file mode 100644
index 0000000..4b618d9
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/test/java/deduplicator/OdeRawEncodedTimDeduplicatorTopologyTest.java
@@ -0,0 +1,89 @@
+package deduplicator;
+
+import org.apache.kafka.common.serialization.Serdes;
+import org.apache.kafka.streams.KeyValue;
+import org.apache.kafka.streams.TestInputTopic;
+import org.apache.kafka.streams.TestOutputTopic;
+import org.apache.kafka.streams.Topology;
+import org.apache.kafka.streams.TopologyTestDriver;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+
+import us.dot.its.jpo.deduplicator.DeduplicatorProperties;
+import us.dot.its.jpo.deduplicator.deduplicator.topologies.OdeRawEncodedTimDeduplicatorTopology;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.List;
+
+
+public class OdeRawEncodedTimDeduplicatorTopologyTest {
+
+ String inputTopic = "topic.OdeRawEncodedTIMJson";
+ String outputTopic = "topic.DeduplicatedOdeRawEncodedTIMJson";
+
+ ObjectMapper objectMapper = new ObjectMapper();
+
+ // Original Message
+ String inputTim1 = "{ \"metadata\": { \"securityResultCode\": \"success\", \"recordGeneratedBy\": \"RSU\", \"schemaVersion\": 6, \"payloadType\": \"us.dot.its.jpo.ode.model.OdeAsn1Payload\", \"serialId\": { \"recordId\": 0, \"serialNumber\": 0, \"streamId\": \"5f470323-7770-4810-b225-8c45aa672103\", \"bundleSize\": 1, \"bundleId\": 0 }, \"sanitized\": false, \"recordType\": \"timMsg\", \"maxDurationTime\": 0, \"odeReceivedAt\": \"2024-07-22T23:23:29.553Z\", \"originIp\": \"10.16.28.53\" }, \"payload\": { \"data\": { \"bytes\": \"001F63701409FF38D05CD47AF567A4570F775D9B0301C269D16DD9656F9637FFF93F421D3B001EA007F99937E1CF5AD1BB0BF4A9D5BEC5BB25CC5B2E64E173162DA00000000269D16DD9656F9631388C100021000EBF7272441F8CFDED60004008027BBAECD8A1A81EF1C153853DD08B394DDCE85F7F4F2222BE087C98000801004F775D9B002F378A81FD1358540F893502C0B711D815FF7883E0A9AACF804536FC9E2A39A67D4289155859ABA8ACBD4997855D345BA429991568E1CA702BA1D8ADFF4805456C0A46862B7F41BE614A122DFB8B0FA019FC52FC8AFF62A7DA3D3F1C9A00BC220AA6B0DC20571A23C142A8C0368F14D181D9B8E2FA859714531606FF8197DC2D81469C97540A33D8D25850D7CA7E82916C1F82142D3A8A20A4884311C320C8EB2290AD1BAC0C84856E8A1B1D760C51BE458B8189591FF0C64E68D0004008027BBAECD8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\" }, \"dataType\": \"us.dot.its.jpo.ode.model.OdeHexByteArray\" }}";
+
+ // Shifted Forward .1 seconds - Should be deduplicated
+ String inputTim2 = "{ \"metadata\": { \"securityResultCode\": \"success\", \"recordGeneratedBy\": \"RSU\", \"schemaVersion\": 6, \"payloadType\": \"us.dot.its.jpo.ode.model.OdeAsn1Payload\", \"serialId\": { \"recordId\": 0, \"serialNumber\": 0, \"streamId\": \"5f470323-7770-4810-b225-8c45aa672103\", \"bundleSize\": 1, \"bundleId\": 0 }, \"sanitized\": false, \"recordType\": \"timMsg\", \"maxDurationTime\": 0, \"odeReceivedAt\": \"2024-07-22T23:23:29.653Z\", \"originIp\": \"10.16.28.53\" }, \"payload\": { \"data\": { \"bytes\": \"001F63701409FF38D05CD47AF567A4570F775D9B0301C269D16DD9656F9637FFF93F421D3B001EA007F99937E1CF5AD1BB0BF4A9D5BEC5BB25CC5B2E64E173162DA00000000269D16DD9656F9631388C100021000EBF7272441F8CFDED60004008027BBAECD8A1A81EF1C153853DD08B394DDCE85F7F4F2222BE087C98000801004F775D9B002F378A81FD1358540F893502C0B711D815FF7883E0A9AACF804536FC9E2A39A67D4289155859ABA8ACBD4997855D345BA429991568E1CA702BA1D8ADFF4805456C0A46862B7F41BE614A122DFB8B0FA019FC52FC8AFF62A7DA3D3F1C9A00BC220AA6B0DC20571A23C142A8C0368F14D181D9B8E2FA859714531606FF8197DC2D81469C97540A33D8D25850D7CA7E82916C1F82142D3A8A20A4884311C320C8EB2290AD1BAC0C84856E8A1B1D760C51BE458B8189591FF0C64E68D0004008027BBAECD8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\" }, \"dataType\": \"us.dot.its.jpo.ode.model.OdeHexByteArray\" }}";
+
+ // Shifted Forward 1 hour Should be allowed to pass through
+ String inputTim3 = "{ \"metadata\": { \"securityResultCode\": \"success\", \"recordGeneratedBy\": \"RSU\", \"schemaVersion\": 6, \"payloadType\": \"us.dot.its.jpo.ode.model.OdeAsn1Payload\", \"serialId\": { \"recordId\": 0, \"serialNumber\": 0, \"streamId\": \"5f470323-7770-4810-b225-8c45aa672103\", \"bundleSize\": 1, \"bundleId\": 0 }, \"sanitized\": false, \"recordType\": \"timMsg\", \"maxDurationTime\": 0, \"odeReceivedAt\": \"2024-07-23T00:23:29.653Z\", \"originIp\": \"10.16.28.53\" }, \"payload\": { \"data\": { \"bytes\": \"001F63701409FF38D05CD47AF567A4570F775D9B0301C269D16DD9656F9637FFF93F421D3B001EA007F99937E1CF5AD1BB0BF4A9D5BEC5BB25CC5B2E64E173162DA00000000269D16DD9656F9631388C100021000EBF7272441F8CFDED60004008027BBAECD8A1A81EF1C153853DD08B394DDCE85F7F4F2222BE087C98000801004F775D9B002F378A81FD1358540F893502C0B711D815FF7883E0A9AACF804536FC9E2A39A67D4289155859ABA8ACBD4997855D345BA429991568E1CA702BA1D8ADFF4805456C0A46862B7F41BE614A122DFB8B0FA019FC52FC8AFF62A7DA3D3F1C9A00BC220AA6B0DC20571A23C142A8C0368F14D181D9B8E2FA859714531606FF8197DC2D81469C97540A33D8D25850D7CA7E82916C1F82142D3A8A20A4884311C320C8EB2290AD1BAC0C84856E8A1B1D760C51BE458B8189591FF0C64E68D0004008027BBAECD8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\" }, \"dataType\": \"us.dot.its.jpo.ode.model.OdeHexByteArray\" }}";
+
+ // Has a different payload ID. Should be allowed through
+ String inputTim4 = "{ \"metadata\": { \"securityResultCode\": \"success\", \"recordGeneratedBy\": \"RSU\", \"schemaVersion\": 6, \"payloadType\": \"us.dot.its.jpo.ode.model.OdeAsn1Payload\", \"serialId\": { \"recordId\": 0, \"serialNumber\": 0, \"streamId\": \"5f470323-7770-4810-b225-8c45aa672103\", \"bundleSize\": 1, \"bundleId\": 0 }, \"sanitized\": false, \"recordType\": \"timMsg\", \"maxDurationTime\": 0, \"odeReceivedAt\": \"2024-07-22T23:23:29.553Z\", \"originIp\": \"10.16.28.53\" }, \"payload\": { \"data\": { \"bytes\": \"001F63701409FF38D05CD47AF567A4570F775D9B0301C269D16DD9656F9637FFF93F421D3B001EA007F99937E1CF5AD1BB0BF4A9D5BEC5BB25CC5B2E64E173162DA00000000269D16DD9656F9631388C100021000EBF7272441F8CFDED60004008027BBAECD8A1A81EF1C153853DD08B394DDCE85F7F4F2222BE087C98000801004F775D9B002F378A81FD1358540F893502C0B711D815FF7883E0A9AACF804536FC9E2A39A67D4289155859ABA8ACBD4997855D345BA429991568E1CA702BA1D8ADFF4805456C0A46862B7F41BE614A122DFB8B0FA019FC52FC8AFF62A7DA3D3F1C9A00BC220AA6B0DC20571A23C142A8C0368F14D181D9B8E2FA859714531606FF8197DC2D81469C97540A33D8D25850D7CA7E82916C1F82142D3A8A20A4884311C320C8EB2290AD1BAC0C84856E8A1B1D760C51BE458B8189591FF0C64E68D0004008027BBAECD9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\" }, \"dataType\": \"us.dot.its.jpo.ode.model.OdeHexByteArray\" }}";
+
+ @Autowired
+ DeduplicatorProperties props;
+
+ @Test
+ public void testTopology() {
+
+ props = new DeduplicatorProperties();
+ props.setKafkaTopicOdeRawEncodedTimJson(inputTopic);
+ props.setKafkaTopicDeduplicatedOdeRawEncodedTimJson(outputTopic);
+
+ OdeRawEncodedTimDeduplicatorTopology TimDeduplicatorTopology = new OdeRawEncodedTimDeduplicatorTopology(props, null);
+
+ Topology topology = TimDeduplicatorTopology.buildTopology();
+ objectMapper.registerModule(new JavaTimeModule());
+
+ try (TopologyTestDriver driver = new TopologyTestDriver(topology)) {
+
+
+ TestInputTopic inputTimData = driver.createInputTopic(
+ inputTopic,
+ Serdes.Void().serializer(),
+ Serdes.String().serializer());
+
+
+ TestOutputTopic outputTimData = driver.createOutputTopic(
+ outputTopic,
+ Serdes.String().deserializer(),
+ Serdes.String().deserializer());
+
+ inputTimData.pipeInput(null, inputTim1);
+ inputTimData.pipeInput(null, inputTim2);
+ inputTimData.pipeInput(null, inputTim3);
+ inputTimData.pipeInput(null, inputTim4);
+
+ List> timDeduplicatedResults = outputTimData.readKeyValuesToList();
+
+ // validate that only 3 messages make it through
+ assertEquals(3, timDeduplicatedResults.size());
+ inputTim1 = inputTim1.strip();
+
+ assertEquals(inputTim1.replace(" ", ""), timDeduplicatedResults.get(0).value.replace(" ", ""));
+ assertEquals(inputTim3.replace(" ", ""), timDeduplicatedResults.get(1).value.replace(" ", ""));
+ assertEquals(inputTim4.replace(" ", ""), timDeduplicatedResults.get(2).value.replace(" ", ""));
+
+ }catch(Exception e){
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/jpo-deduplicator/jpo-deduplicator/src/test/java/deduplicator/ProcessedMapDeduplicatorTopologyTest.java b/jpo-deduplicator/jpo-deduplicator/src/test/java/deduplicator/ProcessedMapDeduplicatorTopologyTest.java
new file mode 100644
index 0000000..331bfdc
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/test/java/deduplicator/ProcessedMapDeduplicatorTopologyTest.java
@@ -0,0 +1,99 @@
+package deduplicator;
+
+import org.apache.kafka.common.serialization.Serdes;
+import org.apache.kafka.streams.KeyValue;
+import org.apache.kafka.streams.TestInputTopic;
+import org.apache.kafka.streams.TestOutputTopic;
+import org.apache.kafka.streams.Topology;
+import org.apache.kafka.streams.TopologyTestDriver;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+
+import us.dot.its.jpo.deduplicator.DeduplicatorProperties;
+import us.dot.its.jpo.deduplicator.deduplicator.topologies.ProcessedMapDeduplicatorTopology;
+import us.dot.its.jpo.geojsonconverter.pojos.geojson.LineString;
+import us.dot.its.jpo.geojsonconverter.pojos.geojson.map.ProcessedMap;
+import us.dot.its.jpo.geojsonconverter.serialization.JsonSerdes;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.List;
+
+
+public class ProcessedMapDeduplicatorTopologyTest {
+
+ String inputTopic = "topic.ProcessedMap";
+ String outputTopic = "topic.DeduplicatedProcessedMap";
+
+
+ TypeReference> typeReference = new TypeReference<>(){};
+ ObjectMapper objectMapper = new ObjectMapper();
+
+ String inputProcessedMap1 = "{\"mapFeatureCollection\":{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"id\":1,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907089,39.587905],[-105.0906245,39.5876246],[-105.0905203,39.587281],[-105.0904383,39.5870554],[-105.0903588,39.5868383],[-105.0902622,39.5865865],[-105.0901249,39.5862612],[-105.0900451,39.5860819],[-105.0899283,39.5858283],[-105.0898739,39.5857117],[-105.0895814,39.5851569],[-105.0888764,39.5839527]]},\"properties\":{\"nodes\":[{\"delta\":[1511,-1514]},{\"delta\":[723,-3116],\"delevation\":10},{\"delta\":[892,-3818],\"delevation\":20},{\"delta\":[702,-2507],\"delevation\":20},{\"delta\":[681,-2412],\"delevation\":10},{\"delta\":[827,-2798],\"delevation\":10},{\"delta\":[1176,-3614],\"delevation\":20},{\"delta\":[683,-1992]},{\"delta\":[1000,-2818],\"delevation\":10},{\"delta\":[466,-1295],\"delevation\":20},{\"delta\":[2505,-6164],\"delevation\":20},{\"delta\":[6037,-13380],\"delevation\":70}],\"laneId\":1,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":1,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":15,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":2,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":2,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907462,39.5878956],[-105.090652,39.5875596],[-105.090534,39.5871793],[-105.0903457,39.5866864],[-105.0902123,39.5863581],[-105.0900802,39.5860572],[-105.0898164,39.5855019],[-105.0895409,39.5849856],[-105.088922,39.5839259]]},\"properties\":{\"nodes\":[{\"delta\":[1192,-1619]},{\"delta\":[807,-3733],\"delevation\":30},{\"delta\":[1010,-4226],\"delevation\":10},{\"delta\":[1612,-5477],\"delevation\":30},{\"delta\":[1142,-3648],\"delevation\":20},{\"delta\":[1131,-3343],\"delevation\":10},{\"delta\":[2259,-6170],\"delevation\":30},{\"delta\":[2359,-5737],\"delevation\":30},{\"delta\":[5300,-11774],\"delevation\":50}],\"laneId\":2,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":1,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":14,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":2,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":3,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907914,39.5878879],[-105.090747,39.5877247],[-105.0906498,39.5874141],[-105.0906262,39.5873356],[-105.0905865,39.5872922]]},\"properties\":{\"nodes\":[{\"delta\":[805,-1704],\"delevation\":10},{\"delta\":[380,-1813]},{\"delta\":[832,-3451],\"delevation\":30},{\"delta\":[202,-872]},{\"delta\":[340,-482],\"delevation\":-10}],\"laneId\":3,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":1,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":10,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":2,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":6,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910008,39.5878477],[-105.0909927,39.5878181]]},\"properties\":{\"nodes\":[{\"delta\":[-988,-2151],\"delevation\":20},{\"delta\":[69,-329]}],\"laneId\":6,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":2,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":5,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.090959,39.5878557],[-105.0909501,39.5878218]]},\"properties\":{\"nodes\":[{\"delta\":[-630,-2062],\"delevation\":10},{\"delta\":[76,-377],\"delevation\":10}],\"laneId\":5,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":2,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":4,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.090914,39.5878612],[-105.0909051,39.5878298]]},\"properties\":{\"nodes\":[{\"delta\":[-245,-2001],\"delevation\":10},{\"delta\":[76,-349]}],\"laneId\":4,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":2,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":10,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911626,39.5880622],[-105.0912043,39.5880536]]},\"properties\":{\"nodes\":[{\"delta\":[-2374,232],\"delevation\":10},{\"delta\":[-357,-96]}],\"laneId\":10,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":4,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":8,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911477,39.587995],[-105.0914565,39.5879427],[-105.0917937,39.5879029],[-105.0922121,39.5878724],[-105.0926509,39.5878748],[-105.0930303,39.5879073],[-105.0932697,39.5879503],[-105.0937243,39.5880569],[-105.0940309,39.5881258],[-105.0943257,39.5881804],[-105.094592,39.5882097]]},\"properties\":{\"nodes\":[{\"delta\":[-2246,-514],\"delevation\":10},{\"delta\":[-2644,-581]},{\"delta\":[-2887,-442],\"delevation\":10},{\"delta\":[-3583,-339],\"delevation\":10},{\"delta\":[-3757,27]},{\"delta\":[-3249,361],\"delevation\":-10},{\"delta\":[-2050,478]},{\"delta\":[-3893,1184]},{\"delta\":[-2625,766],\"delevation\":-10},{\"delta\":[-2524,607],\"delevation\":10},{\"delta\":[-2280,325],\"delevation\":10}],\"laneId\":8,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":3,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":15,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":4,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":7,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911442,39.5879589],[-105.0914154,39.5879165],[-105.0916346,39.5878851],[-105.0918433,39.5878639],[-105.0921546,39.5878547]]},\"properties\":{\"nodes\":[{\"delta\":[-2216,-915],\"delevation\":10},{\"delta\":[-2322,-471]},{\"delta\":[-1877,-349],\"delevation\":10},{\"delta\":[-1787,-235]},{\"delta\":[-2666,-102],\"delevation\":10}],\"laneId\":7,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":3,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":6,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":9,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911534,39.5880261],[-105.091436,39.5879812],[-105.0916658,39.5879507],[-105.091881,39.5879277],[-105.0921287,39.5878972]]},\"properties\":{\"nodes\":[{\"delta\":[-2295,-169],\"delevation\":10},{\"delta\":[-2420,-499]},{\"delta\":[-1968,-339],\"delevation\":10},{\"delta\":[-1843,-256]},{\"delta\":[-2121,-339]}],\"laneId\":9,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":3,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":14,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":4,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":12,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910447,39.5881948],[-105.0911481,39.5886317],[-105.091196,39.588862],[-105.0912349,39.5890282],[-105.0912722,39.5893202],[-105.0913306,39.5897261],[-105.0913695,39.5900324],[-105.0914008,39.5903008],[-105.0914893,39.5913099],[-105.091527,39.5923157]]},\"properties\":{\"nodes\":[{\"delta\":[-1364,1705],\"delevation\":10},{\"delta\":[-885,4854],\"delevation\":-30},{\"delta\":[-410,2559],\"delevation\":10},{\"delta\":[-333,1847],\"delevation\":-10},{\"delta\":[-319,3244],\"delevation\":-20},{\"delta\":[-500,4510]},{\"delta\":[-333,3403],\"delevation\":-30},{\"delta\":[-268,2982]},{\"delta\":[-758,11212],\"delevation\":-30},{\"delta\":[-323,11176],\"delevation\":-70}],\"laneId\":12,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":5,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":5,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":6,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":13,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910013,39.5881975],[-105.0911059,39.5886309],[-105.091144,39.5888313],[-105.0911829,39.5890442],[-105.0912308,39.5893169],[-105.0912689,39.5895877],[-105.0913005,39.5898143],[-105.0913313,39.5900714],[-105.0913597,39.5902968],[-105.0914461,39.5913017],[-105.0914756,39.592324]]},\"properties\":{\"nodes\":[{\"delta\":[-992,1735],\"delevation\":10},{\"delta\":[-896,4816],\"delevation\":-30},{\"delta\":[-326,2227],\"delevation\":10},{\"delta\":[-333,2366]},{\"delta\":[-410,3030],\"delevation\":-20},{\"delta\":[-326,3009],\"delevation\":-10},{\"delta\":[-271,2518],\"delevation\":-10},{\"delta\":[-264,2857],\"delevation\":-20},{\"delta\":[-243,2504]},{\"delta\":[-740,11165],\"delevation\":-30},{\"delta\":[-253,11359],\"delevation\":-70}],\"laneId\":13,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":5,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":4,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":6,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":11,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910891,39.5881859],[-105.0911549,39.5884681],[-105.091196,39.5886783],[-105.091222,39.5888049],[-105.0912401,39.5889649]]},\"properties\":{\"nodes\":[{\"delta\":[-1744,1607],\"delevation\":10},{\"delta\":[-563,3136],\"delevation\":-20},{\"delta\":[-352,2336],\"delevation\":-10},{\"delta\":[-223,1407],\"delevation\":10},{\"delta\":[-155,1778]}],\"laneId\":11,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":5,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":10,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":14,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0908389,39.5882151],[-105.0908478,39.5882471]]},\"properties\":{\"nodes\":[{\"delta\":[398,1931],\"delevation\":-10},{\"delta\":[-76,356]}],\"laneId\":14,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":6,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":15,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907875,39.58822],[-105.0907979,39.5882514]]},\"properties\":{\"nodes\":[{\"delta\":[838,1985],\"delevation\":-20},{\"delta\":[-89,349]}],\"laneId\":15,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":6,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}}]},\"connectingLanesFeatureCollection\":{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"id\":\"1-15\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907089,39.587905],[-105.0907875,39.58822]]},\"properties\":{\"signalGroupId\":2,\"ingressLaneId\":1,\"egressLaneId\":15}},{\"type\":\"Feature\",\"id\":\"2-14\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907462,39.5878956],[-105.0908389,39.5882151]]},\"properties\":{\"signalGroupId\":2,\"ingressLaneId\":2,\"egressLaneId\":14}},{\"type\":\"Feature\",\"id\":\"3-10\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907914,39.5878879],[-105.0911626,39.5880622]]},\"properties\":{\"signalGroupId\":2,\"ingressLaneId\":3,\"egressLaneId\":10}},{\"type\":\"Feature\",\"id\":\"8-15\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911477,39.587995],[-105.0907875,39.58822]]},\"properties\":{\"signalGroupId\":4,\"ingressLaneId\":8,\"egressLaneId\":15}},{\"type\":\"Feature\",\"id\":\"7-6\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911442,39.5879589],[-105.0910008,39.5878477]]},\"properties\":{\"ingressLaneId\":7,\"egressLaneId\":6}},{\"type\":\"Feature\",\"id\":\"9-14\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911534,39.5880261],[-105.0908389,39.5882151]]},\"properties\":{\"signalGroupId\":4,\"ingressLaneId\":9,\"egressLaneId\":14}},{\"type\":\"Feature\",\"id\":\"12-5\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910447,39.5881948],[-105.090959,39.5878557]]},\"properties\":{\"signalGroupId\":6,\"ingressLaneId\":12,\"egressLaneId\":5}},{\"type\":\"Feature\",\"id\":\"13-4\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910013,39.5881975],[-105.090914,39.5878612]]},\"properties\":{\"signalGroupId\":6,\"ingressLaneId\":13,\"egressLaneId\":4}},{\"type\":\"Feature\",\"id\":\"11-10\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910891,39.5881859],[-105.0911626,39.5880622]]},\"properties\":{\"ingressLaneId\":11,\"egressLaneId\":10}}]},\"properties\":{\"messageType\":\"MAP\",\"odeReceivedAt\":\"2024-02-22T23:26:21.06Z\",\"originIp\":\"10.11.81.12\",\"intersectionId\":12109,\"msgIssueRevision\":2,\"revision\":2,\"refPoint\":{\"latitude\":39.5880413,\"longitude\":-105.0908854,\"elevation\":1691},\"cti4501Conformant\":false,\"validationMessages\":[{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].id.region: null found, integer expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].id.region\",\"schemaPath\":\"#/$defs/J2735RoadRegulatorID/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[3].connectsTo: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[3].connectsTo\",\"schemaPath\":\"#/$defs/J2735ConnectsToList_Wrapper/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[3].maneuvers: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[3].maneuvers\",\"schemaPath\":\"#/$defs/J2735AllowedManeuvers/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].connectsTo: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].connectsTo\",\"schemaPath\":\"#/$defs/J2735ConnectsToList_Wrapper/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].maneuvers: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].maneuvers\",\"schemaPath\":\"#/$defs/J2735AllowedManeuvers/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].connectsTo: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].connectsTo\",\"schemaPath\":\"#/$defs/J2735ConnectsToList_Wrapper/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].maneuvers: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].maneuvers\",\"schemaPath\":\"#/$defs/J2735AllowedManeuvers/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].connectsTo: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].connectsTo\",\"schemaPath\":\"#/$defs/J2735ConnectsToList_Wrapper/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].maneuvers: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].maneuvers\",\"schemaPath\":\"#/$defs/J2735AllowedManeuvers/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[8].connectsTo.connectsTo[0].signalGroup: null found, integer expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[8].connectsTo.connectsTo[0].signalGroup\",\"schemaPath\":\"#/$defs/J2735SignalGroupID/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[12].connectsTo.connectsTo[0].signalGroup: null found, integer expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[12].connectsTo.connectsTo[0].signalGroup\",\"schemaPath\":\"#/$defs/J2735SignalGroupID/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[13].connectsTo: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[13].connectsTo\",\"schemaPath\":\"#/$defs/J2735ConnectsToList_Wrapper/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[13].maneuvers: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[13].maneuvers\",\"schemaPath\":\"#/$defs/J2735AllowedManeuvers/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[14].connectsTo: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[14].connectsTo\",\"schemaPath\":\"#/$defs/J2735ConnectsToList_Wrapper/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[14].maneuvers: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[14].maneuvers\",\"schemaPath\":\"#/$defs/J2735AllowedManeuvers/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].speedLimits: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].speedLimits\",\"schemaPath\":\"#/$defs/J2735SpeedLimitList_Wrapper/type\"}],\"laneWidth\":366,\"mapSource\":\"RSU\",\"timeStamp\":\"2024-02-22T23:26:21.06Z\"}}";
+ String inputProcessedMap2 = "{\"mapFeatureCollection\":{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"id\":1,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907089,39.587905],[-105.0906245,39.5876246],[-105.0905203,39.587281],[-105.0904383,39.5870554],[-105.0903588,39.5868383],[-105.0902622,39.5865865],[-105.0901249,39.5862612],[-105.0900451,39.5860819],[-105.0899283,39.5858283],[-105.0898739,39.5857117],[-105.0895814,39.5851569],[-105.0888764,39.5839527]]},\"properties\":{\"nodes\":[{\"delta\":[1511,-1514]},{\"delta\":[723,-3116],\"delevation\":10},{\"delta\":[892,-3818],\"delevation\":20},{\"delta\":[702,-2507],\"delevation\":20},{\"delta\":[681,-2412],\"delevation\":10},{\"delta\":[827,-2798],\"delevation\":10},{\"delta\":[1176,-3614],\"delevation\":20},{\"delta\":[683,-1992]},{\"delta\":[1000,-2818],\"delevation\":10},{\"delta\":[466,-1295],\"delevation\":20},{\"delta\":[2505,-6164],\"delevation\":20},{\"delta\":[6037,-13380],\"delevation\":70}],\"laneId\":1,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":1,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":15,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":2,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":2,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907462,39.5878956],[-105.090652,39.5875596],[-105.090534,39.5871793],[-105.0903457,39.5866864],[-105.0902123,39.5863581],[-105.0900802,39.5860572],[-105.0898164,39.5855019],[-105.0895409,39.5849856],[-105.088922,39.5839259]]},\"properties\":{\"nodes\":[{\"delta\":[1192,-1619]},{\"delta\":[807,-3733],\"delevation\":30},{\"delta\":[1010,-4226],\"delevation\":10},{\"delta\":[1612,-5477],\"delevation\":30},{\"delta\":[1142,-3648],\"delevation\":20},{\"delta\":[1131,-3343],\"delevation\":10},{\"delta\":[2259,-6170],\"delevation\":30},{\"delta\":[2359,-5737],\"delevation\":30},{\"delta\":[5300,-11774],\"delevation\":50}],\"laneId\":2,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":1,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":14,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":2,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":3,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907914,39.5878879],[-105.090747,39.5877247],[-105.0906498,39.5874141],[-105.0906262,39.5873356],[-105.0905865,39.5872922]]},\"properties\":{\"nodes\":[{\"delta\":[805,-1704],\"delevation\":10},{\"delta\":[380,-1813]},{\"delta\":[832,-3451],\"delevation\":30},{\"delta\":[202,-872]},{\"delta\":[340,-482],\"delevation\":-10}],\"laneId\":3,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":1,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":10,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":2,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":6,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910008,39.5878477],[-105.0909927,39.5878181]]},\"properties\":{\"nodes\":[{\"delta\":[-988,-2151],\"delevation\":20},{\"delta\":[69,-329]}],\"laneId\":6,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":2,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":5,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.090959,39.5878557],[-105.0909501,39.5878218]]},\"properties\":{\"nodes\":[{\"delta\":[-630,-2062],\"delevation\":10},{\"delta\":[76,-377],\"delevation\":10}],\"laneId\":5,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":2,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":4,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.090914,39.5878612],[-105.0909051,39.5878298]]},\"properties\":{\"nodes\":[{\"delta\":[-245,-2001],\"delevation\":10},{\"delta\":[76,-349]}],\"laneId\":4,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":2,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":10,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911626,39.5880622],[-105.0912043,39.5880536]]},\"properties\":{\"nodes\":[{\"delta\":[-2374,232],\"delevation\":10},{\"delta\":[-357,-96]}],\"laneId\":10,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":4,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":8,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911477,39.587995],[-105.0914565,39.5879427],[-105.0917937,39.5879029],[-105.0922121,39.5878724],[-105.0926509,39.5878748],[-105.0930303,39.5879073],[-105.0932697,39.5879503],[-105.0937243,39.5880569],[-105.0940309,39.5881258],[-105.0943257,39.5881804],[-105.094592,39.5882097]]},\"properties\":{\"nodes\":[{\"delta\":[-2246,-514],\"delevation\":10},{\"delta\":[-2644,-581]},{\"delta\":[-2887,-442],\"delevation\":10},{\"delta\":[-3583,-339],\"delevation\":10},{\"delta\":[-3757,27]},{\"delta\":[-3249,361],\"delevation\":-10},{\"delta\":[-2050,478]},{\"delta\":[-3893,1184]},{\"delta\":[-2625,766],\"delevation\":-10},{\"delta\":[-2524,607],\"delevation\":10},{\"delta\":[-2280,325],\"delevation\":10}],\"laneId\":8,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":3,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":15,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":4,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":7,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911442,39.5879589],[-105.0914154,39.5879165],[-105.0916346,39.5878851],[-105.0918433,39.5878639],[-105.0921546,39.5878547]]},\"properties\":{\"nodes\":[{\"delta\":[-2216,-915],\"delevation\":10},{\"delta\":[-2322,-471]},{\"delta\":[-1877,-349],\"delevation\":10},{\"delta\":[-1787,-235]},{\"delta\":[-2666,-102],\"delevation\":10}],\"laneId\":7,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":3,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":6,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":9,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911534,39.5880261],[-105.091436,39.5879812],[-105.0916658,39.5879507],[-105.091881,39.5879277],[-105.0921287,39.5878972]]},\"properties\":{\"nodes\":[{\"delta\":[-2295,-169],\"delevation\":10},{\"delta\":[-2420,-499]},{\"delta\":[-1968,-339],\"delevation\":10},{\"delta\":[-1843,-256]},{\"delta\":[-2121,-339]}],\"laneId\":9,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":3,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":14,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":4,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":12,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910447,39.5881948],[-105.0911481,39.5886317],[-105.091196,39.588862],[-105.0912349,39.5890282],[-105.0912722,39.5893202],[-105.0913306,39.5897261],[-105.0913695,39.5900324],[-105.0914008,39.5903008],[-105.0914893,39.5913099],[-105.091527,39.5923157]]},\"properties\":{\"nodes\":[{\"delta\":[-1364,1705],\"delevation\":10},{\"delta\":[-885,4854],\"delevation\":-30},{\"delta\":[-410,2559],\"delevation\":10},{\"delta\":[-333,1847],\"delevation\":-10},{\"delta\":[-319,3244],\"delevation\":-20},{\"delta\":[-500,4510]},{\"delta\":[-333,3403],\"delevation\":-30},{\"delta\":[-268,2982]},{\"delta\":[-758,11212],\"delevation\":-30},{\"delta\":[-323,11176],\"delevation\":-70}],\"laneId\":12,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":5,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":5,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":6,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":13,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910013,39.5881975],[-105.0911059,39.5886309],[-105.091144,39.5888313],[-105.0911829,39.5890442],[-105.0912308,39.5893169],[-105.0912689,39.5895877],[-105.0913005,39.5898143],[-105.0913313,39.5900714],[-105.0913597,39.5902968],[-105.0914461,39.5913017],[-105.0914756,39.592324]]},\"properties\":{\"nodes\":[{\"delta\":[-992,1735],\"delevation\":10},{\"delta\":[-896,4816],\"delevation\":-30},{\"delta\":[-326,2227],\"delevation\":10},{\"delta\":[-333,2366]},{\"delta\":[-410,3030],\"delevation\":-20},{\"delta\":[-326,3009],\"delevation\":-10},{\"delta\":[-271,2518],\"delevation\":-10},{\"delta\":[-264,2857],\"delevation\":-20},{\"delta\":[-243,2504]},{\"delta\":[-740,11165],\"delevation\":-30},{\"delta\":[-253,11359],\"delevation\":-70}],\"laneId\":13,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":5,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":4,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":6,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":11,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910891,39.5881859],[-105.0911549,39.5884681],[-105.091196,39.5886783],[-105.091222,39.5888049],[-105.0912401,39.5889649]]},\"properties\":{\"nodes\":[{\"delta\":[-1744,1607],\"delevation\":10},{\"delta\":[-563,3136],\"delevation\":-20},{\"delta\":[-352,2336],\"delevation\":-10},{\"delta\":[-223,1407],\"delevation\":10},{\"delta\":[-155,1778]}],\"laneId\":11,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":5,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":10,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":14,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0908389,39.5882151],[-105.0908478,39.5882471]]},\"properties\":{\"nodes\":[{\"delta\":[398,1931],\"delevation\":-10},{\"delta\":[-76,356]}],\"laneId\":14,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":6,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":15,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907875,39.58822],[-105.0907979,39.5882514]]},\"properties\":{\"nodes\":[{\"delta\":[838,1985],\"delevation\":-20},{\"delta\":[-89,349]}],\"laneId\":15,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":6,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}}]},\"connectingLanesFeatureCollection\":{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"id\":\"1-15\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907089,39.587905],[-105.0907875,39.58822]]},\"properties\":{\"signalGroupId\":2,\"ingressLaneId\":1,\"egressLaneId\":15}},{\"type\":\"Feature\",\"id\":\"2-14\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907462,39.5878956],[-105.0908389,39.5882151]]},\"properties\":{\"signalGroupId\":2,\"ingressLaneId\":2,\"egressLaneId\":14}},{\"type\":\"Feature\",\"id\":\"3-10\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907914,39.5878879],[-105.0911626,39.5880622]]},\"properties\":{\"signalGroupId\":2,\"ingressLaneId\":3,\"egressLaneId\":10}},{\"type\":\"Feature\",\"id\":\"8-15\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911477,39.587995],[-105.0907875,39.58822]]},\"properties\":{\"signalGroupId\":4,\"ingressLaneId\":8,\"egressLaneId\":15}},{\"type\":\"Feature\",\"id\":\"7-6\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911442,39.5879589],[-105.0910008,39.5878477]]},\"properties\":{\"ingressLaneId\":7,\"egressLaneId\":6}},{\"type\":\"Feature\",\"id\":\"9-14\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911534,39.5880261],[-105.0908389,39.5882151]]},\"properties\":{\"signalGroupId\":4,\"ingressLaneId\":9,\"egressLaneId\":14}},{\"type\":\"Feature\",\"id\":\"12-5\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910447,39.5881948],[-105.090959,39.5878557]]},\"properties\":{\"signalGroupId\":6,\"ingressLaneId\":12,\"egressLaneId\":5}},{\"type\":\"Feature\",\"id\":\"13-4\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910013,39.5881975],[-105.090914,39.5878612]]},\"properties\":{\"signalGroupId\":6,\"ingressLaneId\":13,\"egressLaneId\":4}},{\"type\":\"Feature\",\"id\":\"11-10\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910891,39.5881859],[-105.0911626,39.5880622]]},\"properties\":{\"ingressLaneId\":11,\"egressLaneId\":10}}]},\"properties\":{\"messageType\":\"MAP\",\"odeReceivedAt\":\"2024-02-22T23:26:22.074Z\",\"originIp\":\"10.11.81.12\",\"intersectionId\":12109,\"msgIssueRevision\":2,\"revision\":2,\"refPoint\":{\"latitude\":39.5880413,\"longitude\":-105.0908854,\"elevation\":1691},\"cti4501Conformant\":false,\"validationMessages\":[{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].id.region: null found, integer expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].id.region\",\"schemaPath\":\"#/$defs/J2735RoadRegulatorID/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[3].connectsTo: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[3].connectsTo\",\"schemaPath\":\"#/$defs/J2735ConnectsToList_Wrapper/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[3].maneuvers: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[3].maneuvers\",\"schemaPath\":\"#/$defs/J2735AllowedManeuvers/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].connectsTo: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].connectsTo\",\"schemaPath\":\"#/$defs/J2735ConnectsToList_Wrapper/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].maneuvers: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].maneuvers\",\"schemaPath\":\"#/$defs/J2735AllowedManeuvers/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].connectsTo: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].connectsTo\",\"schemaPath\":\"#/$defs/J2735ConnectsToList_Wrapper/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].maneuvers: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].maneuvers\",\"schemaPath\":\"#/$defs/J2735AllowedManeuvers/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].connectsTo: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].connectsTo\",\"schemaPath\":\"#/$defs/J2735ConnectsToList_Wrapper/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].maneuvers: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].maneuvers\",\"schemaPath\":\"#/$defs/J2735AllowedManeuvers/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[8].connectsTo.connectsTo[0].signalGroup: null found, integer expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[8].connectsTo.connectsTo[0].signalGroup\",\"schemaPath\":\"#/$defs/J2735SignalGroupID/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[12].connectsTo.connectsTo[0].signalGroup: null found, integer expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[12].connectsTo.connectsTo[0].signalGroup\",\"schemaPath\":\"#/$defs/J2735SignalGroupID/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[13].connectsTo: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[13].connectsTo\",\"schemaPath\":\"#/$defs/J2735ConnectsToList_Wrapper/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[13].maneuvers: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[13].maneuvers\",\"schemaPath\":\"#/$defs/J2735AllowedManeuvers/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[14].connectsTo: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[14].connectsTo\",\"schemaPath\":\"#/$defs/J2735ConnectsToList_Wrapper/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[14].maneuvers: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[14].maneuvers\",\"schemaPath\":\"#/$defs/J2735AllowedManeuvers/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].speedLimits: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].speedLimits\",\"schemaPath\":\"#/$defs/J2735SpeedLimitList_Wrapper/type\"}],\"laneWidth\":366,\"mapSource\":\"RSU\",\"timeStamp\":\"2024-02-22T23:26:22.074Z\"}}";
+ String inputProcessedMap3 = "{\"mapFeatureCollection\":{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"id\":1,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907089,39.587905],[-105.0906245,39.5876246],[-105.0905203,39.587281],[-105.0904383,39.5870554],[-105.0903588,39.5868383],[-105.0902622,39.5865865],[-105.0901249,39.5862612],[-105.0900451,39.5860819],[-105.0899283,39.5858283],[-105.0898739,39.5857117],[-105.0895814,39.5851569],[-105.0888764,39.5839527]]},\"properties\":{\"nodes\":[{\"delta\":[1511,-1514]},{\"delta\":[723,-3116],\"delevation\":10},{\"delta\":[892,-3818],\"delevation\":20},{\"delta\":[702,-2507],\"delevation\":20},{\"delta\":[681,-2412],\"delevation\":10},{\"delta\":[827,-2798],\"delevation\":10},{\"delta\":[1176,-3614],\"delevation\":20},{\"delta\":[683,-1992]},{\"delta\":[1000,-2818],\"delevation\":10},{\"delta\":[466,-1295],\"delevation\":20},{\"delta\":[2505,-6164],\"delevation\":20},{\"delta\":[6037,-13380],\"delevation\":70}],\"laneId\":1,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":1,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":15,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":2,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":2,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907462,39.5878956],[-105.090652,39.5875596],[-105.090534,39.5871793],[-105.0903457,39.5866864],[-105.0902123,39.5863581],[-105.0900802,39.5860572],[-105.0898164,39.5855019],[-105.0895409,39.5849856],[-105.088922,39.5839259]]},\"properties\":{\"nodes\":[{\"delta\":[1192,-1619]},{\"delta\":[807,-3733],\"delevation\":30},{\"delta\":[1010,-4226],\"delevation\":10},{\"delta\":[1612,-5477],\"delevation\":30},{\"delta\":[1142,-3648],\"delevation\":20},{\"delta\":[1131,-3343],\"delevation\":10},{\"delta\":[2259,-6170],\"delevation\":30},{\"delta\":[2359,-5737],\"delevation\":30},{\"delta\":[5300,-11774],\"delevation\":50}],\"laneId\":2,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":1,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":14,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":2,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":3,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907914,39.5878879],[-105.090747,39.5877247],[-105.0906498,39.5874141],[-105.0906262,39.5873356],[-105.0905865,39.5872922]]},\"properties\":{\"nodes\":[{\"delta\":[805,-1704],\"delevation\":10},{\"delta\":[380,-1813]},{\"delta\":[832,-3451],\"delevation\":30},{\"delta\":[202,-872]},{\"delta\":[340,-482],\"delevation\":-10}],\"laneId\":3,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":1,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":10,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":2,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":6,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910008,39.5878477],[-105.0909927,39.5878181]]},\"properties\":{\"nodes\":[{\"delta\":[-988,-2151],\"delevation\":20},{\"delta\":[69,-329]}],\"laneId\":6,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":2,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":5,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.090959,39.5878557],[-105.0909501,39.5878218]]},\"properties\":{\"nodes\":[{\"delta\":[-630,-2062],\"delevation\":10},{\"delta\":[76,-377],\"delevation\":10}],\"laneId\":5,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":2,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":4,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.090914,39.5878612],[-105.0909051,39.5878298]]},\"properties\":{\"nodes\":[{\"delta\":[-245,-2001],\"delevation\":10},{\"delta\":[76,-349]}],\"laneId\":4,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":2,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":10,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911626,39.5880622],[-105.0912043,39.5880536]]},\"properties\":{\"nodes\":[{\"delta\":[-2374,232],\"delevation\":10},{\"delta\":[-357,-96]}],\"laneId\":10,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":4,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":8,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911477,39.587995],[-105.0914565,39.5879427],[-105.0917937,39.5879029],[-105.0922121,39.5878724],[-105.0926509,39.5878748],[-105.0930303,39.5879073],[-105.0932697,39.5879503],[-105.0937243,39.5880569],[-105.0940309,39.5881258],[-105.0943257,39.5881804],[-105.094592,39.5882097]]},\"properties\":{\"nodes\":[{\"delta\":[-2246,-514],\"delevation\":10},{\"delta\":[-2644,-581]},{\"delta\":[-2887,-442],\"delevation\":10},{\"delta\":[-3583,-339],\"delevation\":10},{\"delta\":[-3757,27]},{\"delta\":[-3249,361],\"delevation\":-10},{\"delta\":[-2050,478]},{\"delta\":[-3893,1184]},{\"delta\":[-2625,766],\"delevation\":-10},{\"delta\":[-2524,607],\"delevation\":10},{\"delta\":[-2280,325],\"delevation\":10}],\"laneId\":8,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":3,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":15,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":4,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":7,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911442,39.5879589],[-105.0914154,39.5879165],[-105.0916346,39.5878851],[-105.0918433,39.5878639],[-105.0921546,39.5878547]]},\"properties\":{\"nodes\":[{\"delta\":[-2216,-915],\"delevation\":10},{\"delta\":[-2322,-471]},{\"delta\":[-1877,-349],\"delevation\":10},{\"delta\":[-1787,-235]},{\"delta\":[-2666,-102],\"delevation\":10}],\"laneId\":7,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":3,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":6,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":9,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911534,39.5880261],[-105.091436,39.5879812],[-105.0916658,39.5879507],[-105.091881,39.5879277],[-105.0921287,39.5878972]]},\"properties\":{\"nodes\":[{\"delta\":[-2295,-169],\"delevation\":10},{\"delta\":[-2420,-499]},{\"delta\":[-1968,-339],\"delevation\":10},{\"delta\":[-1843,-256]},{\"delta\":[-2121,-339]}],\"laneId\":9,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":3,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":14,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":4,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":12,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910447,39.5881948],[-105.0911481,39.5886317],[-105.091196,39.588862],[-105.0912349,39.5890282],[-105.0912722,39.5893202],[-105.0913306,39.5897261],[-105.0913695,39.5900324],[-105.0914008,39.5903008],[-105.0914893,39.5913099],[-105.091527,39.5923157]]},\"properties\":{\"nodes\":[{\"delta\":[-1364,1705],\"delevation\":10},{\"delta\":[-885,4854],\"delevation\":-30},{\"delta\":[-410,2559],\"delevation\":10},{\"delta\":[-333,1847],\"delevation\":-10},{\"delta\":[-319,3244],\"delevation\":-20},{\"delta\":[-500,4510]},{\"delta\":[-333,3403],\"delevation\":-30},{\"delta\":[-268,2982]},{\"delta\":[-758,11212],\"delevation\":-30},{\"delta\":[-323,11176],\"delevation\":-70}],\"laneId\":12,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":5,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":5,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":6,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":13,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910013,39.5881975],[-105.0911059,39.5886309],[-105.091144,39.5888313],[-105.0911829,39.5890442],[-105.0912308,39.5893169],[-105.0912689,39.5895877],[-105.0913005,39.5898143],[-105.0913313,39.5900714],[-105.0913597,39.5902968],[-105.0914461,39.5913017],[-105.0914756,39.592324]]},\"properties\":{\"nodes\":[{\"delta\":[-992,1735],\"delevation\":10},{\"delta\":[-896,4816],\"delevation\":-30},{\"delta\":[-326,2227],\"delevation\":10},{\"delta\":[-333,2366]},{\"delta\":[-410,3030],\"delevation\":-20},{\"delta\":[-326,3009],\"delevation\":-10},{\"delta\":[-271,2518],\"delevation\":-10},{\"delta\":[-264,2857],\"delevation\":-20},{\"delta\":[-243,2504]},{\"delta\":[-740,11165],\"delevation\":-30},{\"delta\":[-253,11359],\"delevation\":-70}],\"laneId\":13,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":5,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":4,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":6,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":11,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910891,39.5881859],[-105.0911549,39.5884681],[-105.091196,39.5886783],[-105.091222,39.5888049],[-105.0912401,39.5889649]]},\"properties\":{\"nodes\":[{\"delta\":[-1744,1607],\"delevation\":10},{\"delta\":[-563,3136],\"delevation\":-20},{\"delta\":[-352,2336],\"delevation\":-10},{\"delta\":[-223,1407],\"delevation\":10},{\"delta\":[-155,1778]}],\"laneId\":11,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":5,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":10,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":14,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0908389,39.5882151],[-105.0908478,39.5882471]]},\"properties\":{\"nodes\":[{\"delta\":[398,1931],\"delevation\":-10},{\"delta\":[-76,356]}],\"laneId\":14,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":6,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":15,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907875,39.58822],[-105.0907979,39.5882514]]},\"properties\":{\"nodes\":[{\"delta\":[838,1985],\"delevation\":-20},{\"delta\":[-89,349]}],\"laneId\":15,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":6,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}}]},\"connectingLanesFeatureCollection\":{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"id\":\"1-15\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907089,39.587905],[-105.0907875,39.58822]]},\"properties\":{\"signalGroupId\":2,\"ingressLaneId\":1,\"egressLaneId\":15}},{\"type\":\"Feature\",\"id\":\"2-14\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907462,39.5878956],[-105.0908389,39.5882151]]},\"properties\":{\"signalGroupId\":2,\"ingressLaneId\":2,\"egressLaneId\":14}},{\"type\":\"Feature\",\"id\":\"3-10\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907914,39.5878879],[-105.0911626,39.5880622]]},\"properties\":{\"signalGroupId\":2,\"ingressLaneId\":3,\"egressLaneId\":10}},{\"type\":\"Feature\",\"id\":\"8-15\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911477,39.587995],[-105.0907875,39.58822]]},\"properties\":{\"signalGroupId\":4,\"ingressLaneId\":8,\"egressLaneId\":15}},{\"type\":\"Feature\",\"id\":\"7-6\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911442,39.5879589],[-105.0910008,39.5878477]]},\"properties\":{\"ingressLaneId\":7,\"egressLaneId\":6}},{\"type\":\"Feature\",\"id\":\"9-14\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911534,39.5880261],[-105.0908389,39.5882151]]},\"properties\":{\"signalGroupId\":4,\"ingressLaneId\":9,\"egressLaneId\":14}},{\"type\":\"Feature\",\"id\":\"12-5\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910447,39.5881948],[-105.090959,39.5878557]]},\"properties\":{\"signalGroupId\":6,\"ingressLaneId\":12,\"egressLaneId\":5}},{\"type\":\"Feature\",\"id\":\"13-4\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910013,39.5881975],[-105.090914,39.5878612]]},\"properties\":{\"signalGroupId\":6,\"ingressLaneId\":13,\"egressLaneId\":4}},{\"type\":\"Feature\",\"id\":\"11-10\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910891,39.5881859],[-105.0911626,39.5880622]]},\"properties\":{\"ingressLaneId\":11,\"egressLaneId\":10}}]},\"properties\":{\"messageType\":\"MAP\",\"odeReceivedAt\":\"2024-02-22T23:26:25.107Z\",\"originIp\":\"10.11.81.12\",\"intersectionId\":12109,\"msgIssueRevision\":2,\"revision\":2,\"refPoint\":{\"latitude\":39.5880413,\"longitude\":-105.0908854,\"elevation\":1500},\"cti4501Conformant\":false,\"validationMessages\":[{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].id.region: null found, integer expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].id.region\",\"schemaPath\":\"#/$defs/J2735RoadRegulatorID/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[3].connectsTo: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[3].connectsTo\",\"schemaPath\":\"#/$defs/J2735ConnectsToList_Wrapper/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[3].maneuvers: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[3].maneuvers\",\"schemaPath\":\"#/$defs/J2735AllowedManeuvers/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].connectsTo: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].connectsTo\",\"schemaPath\":\"#/$defs/J2735ConnectsToList_Wrapper/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].maneuvers: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].maneuvers\",\"schemaPath\":\"#/$defs/J2735AllowedManeuvers/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].connectsTo: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].connectsTo\",\"schemaPath\":\"#/$defs/J2735ConnectsToList_Wrapper/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].maneuvers: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].maneuvers\",\"schemaPath\":\"#/$defs/J2735AllowedManeuvers/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].connectsTo: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].connectsTo\",\"schemaPath\":\"#/$defs/J2735ConnectsToList_Wrapper/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].maneuvers: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].maneuvers\",\"schemaPath\":\"#/$defs/J2735AllowedManeuvers/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[8].connectsTo.connectsTo[0].signalGroup: null found, integer expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[8].connectsTo.connectsTo[0].signalGroup\",\"schemaPath\":\"#/$defs/J2735SignalGroupID/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[12].connectsTo.connectsTo[0].signalGroup: null found, integer expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[12].connectsTo.connectsTo[0].signalGroup\",\"schemaPath\":\"#/$defs/J2735SignalGroupID/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[13].connectsTo: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[13].connectsTo\",\"schemaPath\":\"#/$defs/J2735ConnectsToList_Wrapper/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[13].maneuvers: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[13].maneuvers\",\"schemaPath\":\"#/$defs/J2735AllowedManeuvers/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[14].connectsTo: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[14].connectsTo\",\"schemaPath\":\"#/$defs/J2735ConnectsToList_Wrapper/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[14].maneuvers: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[14].maneuvers\",\"schemaPath\":\"#/$defs/J2735AllowedManeuvers/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].speedLimits: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].speedLimits\",\"schemaPath\":\"#/$defs/J2735SpeedLimitList_Wrapper/type\"}],\"laneWidth\":366,\"mapSource\":\"RSU\",\"timeStamp\":\"2024-02-22T23:26:25.107Z\"}}";
+ String inputProcessedMap4 = "{\"mapFeatureCollection\":{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"id\":1,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907089,39.587905],[-105.0906245,39.5876246],[-105.0905203,39.587281],[-105.0904383,39.5870554],[-105.0903588,39.5868383],[-105.0902622,39.5865865],[-105.0901249,39.5862612],[-105.0900451,39.5860819],[-105.0899283,39.5858283],[-105.0898739,39.5857117],[-105.0895814,39.5851569],[-105.0888764,39.5839527]]},\"properties\":{\"nodes\":[{\"delta\":[1511,-1514]},{\"delta\":[723,-3116],\"delevation\":10},{\"delta\":[892,-3818],\"delevation\":20},{\"delta\":[702,-2507],\"delevation\":20},{\"delta\":[681,-2412],\"delevation\":10},{\"delta\":[827,-2798],\"delevation\":10},{\"delta\":[1176,-3614],\"delevation\":20},{\"delta\":[683,-1992]},{\"delta\":[1000,-2818],\"delevation\":10},{\"delta\":[466,-1295],\"delevation\":20},{\"delta\":[2505,-6164],\"delevation\":20},{\"delta\":[6037,-13380],\"delevation\":70}],\"laneId\":1,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":1,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":15,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":2,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":2,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907462,39.5878956],[-105.090652,39.5875596],[-105.090534,39.5871793],[-105.0903457,39.5866864],[-105.0902123,39.5863581],[-105.0900802,39.5860572],[-105.0898164,39.5855019],[-105.0895409,39.5849856],[-105.088922,39.5839259]]},\"properties\":{\"nodes\":[{\"delta\":[1192,-1619]},{\"delta\":[807,-3733],\"delevation\":30},{\"delta\":[1010,-4226],\"delevation\":10},{\"delta\":[1612,-5477],\"delevation\":30},{\"delta\":[1142,-3648],\"delevation\":20},{\"delta\":[1131,-3343],\"delevation\":10},{\"delta\":[2259,-6170],\"delevation\":30},{\"delta\":[2359,-5737],\"delevation\":30},{\"delta\":[5300,-11774],\"delevation\":50}],\"laneId\":2,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":1,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":14,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":2,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":3,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907914,39.5878879],[-105.090747,39.5877247],[-105.0906498,39.5874141],[-105.0906262,39.5873356],[-105.0905865,39.5872922]]},\"properties\":{\"nodes\":[{\"delta\":[805,-1704],\"delevation\":10},{\"delta\":[380,-1813]},{\"delta\":[832,-3451],\"delevation\":30},{\"delta\":[202,-872]},{\"delta\":[340,-482],\"delevation\":-10}],\"laneId\":3,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":1,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":10,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":2,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":6,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910008,39.5878477],[-105.0909927,39.5878181]]},\"properties\":{\"nodes\":[{\"delta\":[-988,-2151],\"delevation\":20},{\"delta\":[69,-329]}],\"laneId\":6,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":2,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":5,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.090959,39.5878557],[-105.0909501,39.5878218]]},\"properties\":{\"nodes\":[{\"delta\":[-630,-2062],\"delevation\":10},{\"delta\":[76,-377],\"delevation\":10}],\"laneId\":5,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":2,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":4,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.090914,39.5878612],[-105.0909051,39.5878298]]},\"properties\":{\"nodes\":[{\"delta\":[-245,-2001],\"delevation\":10},{\"delta\":[76,-349]}],\"laneId\":4,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":2,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":10,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911626,39.5880622],[-105.0912043,39.5880536]]},\"properties\":{\"nodes\":[{\"delta\":[-2374,232],\"delevation\":10},{\"delta\":[-357,-96]}],\"laneId\":10,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":4,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":8,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911477,39.587995],[-105.0914565,39.5879427],[-105.0917937,39.5879029],[-105.0922121,39.5878724],[-105.0926509,39.5878748],[-105.0930303,39.5879073],[-105.0932697,39.5879503],[-105.0937243,39.5880569],[-105.0940309,39.5881258],[-105.0943257,39.5881804],[-105.094592,39.5882097]]},\"properties\":{\"nodes\":[{\"delta\":[-2246,-514],\"delevation\":10},{\"delta\":[-2644,-581]},{\"delta\":[-2887,-442],\"delevation\":10},{\"delta\":[-3583,-339],\"delevation\":10},{\"delta\":[-3757,27]},{\"delta\":[-3249,361],\"delevation\":-10},{\"delta\":[-2050,478]},{\"delta\":[-3893,1184]},{\"delta\":[-2625,766],\"delevation\":-10},{\"delta\":[-2524,607],\"delevation\":10},{\"delta\":[-2280,325],\"delevation\":10}],\"laneId\":8,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":3,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":15,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":4,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":7,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911442,39.5879589],[-105.0914154,39.5879165],[-105.0916346,39.5878851],[-105.0918433,39.5878639],[-105.0921546,39.5878547]]},\"properties\":{\"nodes\":[{\"delta\":[-2216,-915],\"delevation\":10},{\"delta\":[-2322,-471]},{\"delta\":[-1877,-349],\"delevation\":10},{\"delta\":[-1787,-235]},{\"delta\":[-2666,-102],\"delevation\":10}],\"laneId\":7,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":3,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":6,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":9,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911534,39.5880261],[-105.091436,39.5879812],[-105.0916658,39.5879507],[-105.091881,39.5879277],[-105.0921287,39.5878972]]},\"properties\":{\"nodes\":[{\"delta\":[-2295,-169],\"delevation\":10},{\"delta\":[-2420,-499]},{\"delta\":[-1968,-339],\"delevation\":10},{\"delta\":[-1843,-256]},{\"delta\":[-2121,-339]}],\"laneId\":9,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":3,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":14,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":4,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":12,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910447,39.5881948],[-105.0911481,39.5886317],[-105.091196,39.588862],[-105.0912349,39.5890282],[-105.0912722,39.5893202],[-105.0913306,39.5897261],[-105.0913695,39.5900324],[-105.0914008,39.5903008],[-105.0914893,39.5913099],[-105.091527,39.5923157]]},\"properties\":{\"nodes\":[{\"delta\":[-1364,1705],\"delevation\":10},{\"delta\":[-885,4854],\"delevation\":-30},{\"delta\":[-410,2559],\"delevation\":10},{\"delta\":[-333,1847],\"delevation\":-10},{\"delta\":[-319,3244],\"delevation\":-20},{\"delta\":[-500,4510]},{\"delta\":[-333,3403],\"delevation\":-30},{\"delta\":[-268,2982]},{\"delta\":[-758,11212],\"delevation\":-30},{\"delta\":[-323,11176],\"delevation\":-70}],\"laneId\":12,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":5,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":5,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":6,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":13,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910013,39.5881975],[-105.0911059,39.5886309],[-105.091144,39.5888313],[-105.0911829,39.5890442],[-105.0912308,39.5893169],[-105.0912689,39.5895877],[-105.0913005,39.5898143],[-105.0913313,39.5900714],[-105.0913597,39.5902968],[-105.0914461,39.5913017],[-105.0914756,39.592324]]},\"properties\":{\"nodes\":[{\"delta\":[-992,1735],\"delevation\":10},{\"delta\":[-896,4816],\"delevation\":-30},{\"delta\":[-326,2227],\"delevation\":10},{\"delta\":[-333,2366]},{\"delta\":[-410,3030],\"delevation\":-20},{\"delta\":[-326,3009],\"delevation\":-10},{\"delta\":[-271,2518],\"delevation\":-10},{\"delta\":[-264,2857],\"delevation\":-20},{\"delta\":[-243,2504]},{\"delta\":[-740,11165],\"delevation\":-30},{\"delta\":[-253,11359],\"delevation\":-70}],\"laneId\":13,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":5,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":4,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":6,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":11,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910891,39.5881859],[-105.0911549,39.5884681],[-105.091196,39.5886783],[-105.091222,39.5888049],[-105.0912401,39.5889649]]},\"properties\":{\"nodes\":[{\"delta\":[-1744,1607],\"delevation\":10},{\"delta\":[-563,3136],\"delevation\":-20},{\"delta\":[-352,2336],\"delevation\":-10},{\"delta\":[-223,1407],\"delevation\":10},{\"delta\":[-155,1778]}],\"laneId\":11,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":5,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":10,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":14,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0908389,39.5882151],[-105.0908478,39.5882471]]},\"properties\":{\"nodes\":[{\"delta\":[398,1931],\"delevation\":-10},{\"delta\":[-76,356]}],\"laneId\":14,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":6,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":15,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907875,39.58822],[-105.0907979,39.5882514]]},\"properties\":{\"nodes\":[{\"delta\":[838,1985],\"delevation\":-20},{\"delta\":[-89,349]}],\"laneId\":15,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":6,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}}]},\"connectingLanesFeatureCollection\":{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"id\":\"1-15\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907089,39.587905],[-105.0907875,39.58822]]},\"properties\":{\"signalGroupId\":2,\"ingressLaneId\":1,\"egressLaneId\":15}},{\"type\":\"Feature\",\"id\":\"2-14\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907462,39.5878956],[-105.0908389,39.5882151]]},\"properties\":{\"signalGroupId\":2,\"ingressLaneId\":2,\"egressLaneId\":14}},{\"type\":\"Feature\",\"id\":\"3-10\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907914,39.5878879],[-105.0911626,39.5880622]]},\"properties\":{\"signalGroupId\":2,\"ingressLaneId\":3,\"egressLaneId\":10}},{\"type\":\"Feature\",\"id\":\"8-15\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911477,39.587995],[-105.0907875,39.58822]]},\"properties\":{\"signalGroupId\":4,\"ingressLaneId\":8,\"egressLaneId\":15}},{\"type\":\"Feature\",\"id\":\"7-6\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911442,39.5879589],[-105.0910008,39.5878477]]},\"properties\":{\"ingressLaneId\":7,\"egressLaneId\":6}},{\"type\":\"Feature\",\"id\":\"9-14\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911534,39.5880261],[-105.0908389,39.5882151]]},\"properties\":{\"signalGroupId\":4,\"ingressLaneId\":9,\"egressLaneId\":14}},{\"type\":\"Feature\",\"id\":\"12-5\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910447,39.5881948],[-105.090959,39.5878557]]},\"properties\":{\"signalGroupId\":6,\"ingressLaneId\":12,\"egressLaneId\":5}},{\"type\":\"Feature\",\"id\":\"13-4\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910013,39.5881975],[-105.090914,39.5878612]]},\"properties\":{\"signalGroupId\":6,\"ingressLaneId\":13,\"egressLaneId\":4}},{\"type\":\"Feature\",\"id\":\"11-10\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910891,39.5881859],[-105.0911626,39.5880622]]},\"properties\":{\"ingressLaneId\":11,\"egressLaneId\":10}}]},\"properties\":{\"messageType\":\"MAP\",\"odeReceivedAt\":\"2024-02-22T23:26:26.114Z\",\"originIp\":\"10.11.81.12\",\"intersectionId\":12109,\"msgIssueRevision\":2,\"revision\":2,\"refPoint\":{\"latitude\":39.5880413,\"longitude\":-105.0908854,\"elevation\":1691},\"cti4501Conformant\":false,\"validationMessages\":[{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].id.region: null found, integer expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].id.region\",\"schemaPath\":\"#/$defs/J2735RoadRegulatorID/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[3].connectsTo: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[3].connectsTo\",\"schemaPath\":\"#/$defs/J2735ConnectsToList_Wrapper/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[3].maneuvers: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[3].maneuvers\",\"schemaPath\":\"#/$defs/J2735AllowedManeuvers/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].connectsTo: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].connectsTo\",\"schemaPath\":\"#/$defs/J2735ConnectsToList_Wrapper/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].maneuvers: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].maneuvers\",\"schemaPath\":\"#/$defs/J2735AllowedManeuvers/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].connectsTo: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].connectsTo\",\"schemaPath\":\"#/$defs/J2735ConnectsToList_Wrapper/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].maneuvers: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].maneuvers\",\"schemaPath\":\"#/$defs/J2735AllowedManeuvers/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].connectsTo: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].connectsTo\",\"schemaPath\":\"#/$defs/J2735ConnectsToList_Wrapper/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].maneuvers: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].maneuvers\",\"schemaPath\":\"#/$defs/J2735AllowedManeuvers/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[8].connectsTo.connectsTo[0].signalGroup: null found, integer expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[8].connectsTo.connectsTo[0].signalGroup\",\"schemaPath\":\"#/$defs/J2735SignalGroupID/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[12].connectsTo.connectsTo[0].signalGroup: null found, integer expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[12].connectsTo.connectsTo[0].signalGroup\",\"schemaPath\":\"#/$defs/J2735SignalGroupID/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[13].connectsTo: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[13].connectsTo\",\"schemaPath\":\"#/$defs/J2735ConnectsToList_Wrapper/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[13].maneuvers: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[13].maneuvers\",\"schemaPath\":\"#/$defs/J2735AllowedManeuvers/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[14].connectsTo: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[14].connectsTo\",\"schemaPath\":\"#/$defs/J2735ConnectsToList_Wrapper/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[14].maneuvers: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[14].maneuvers\",\"schemaPath\":\"#/$defs/J2735AllowedManeuvers/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].speedLimits: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].speedLimits\",\"schemaPath\":\"#/$defs/J2735SpeedLimitList_Wrapper/type\"}],\"laneWidth\":366,\"mapSource\":\"RSU\",\"timeStamp\":\"2024-02-22T23:26:26.114Z\"}}";
+
+
+ String key = "{\"rsuId\":\"10.11.81.12\",\"intersectionId\":12109,\"region\":-1}";
+
+ @Autowired
+ DeduplicatorProperties props;
+
+
+ @Test
+ public void testTopology() {
+
+ props = new DeduplicatorProperties();
+ props.setKafkaTopicProcessedMap(inputTopic);
+ props.setKafkaTopicDeduplicatedProcessedMap(outputTopic);
+
+ ProcessedMapDeduplicatorTopology processedMapDeduplicatorTopology = new ProcessedMapDeduplicatorTopology(props, null);
+
+ Topology topology = processedMapDeduplicatorTopology.buildTopology();
+ objectMapper.registerModule(new JavaTimeModule());
+
+ try (TopologyTestDriver driver = new TopologyTestDriver(topology)) {
+
+
+ TestInputTopic inputProcessedMapData = driver.createInputTopic(
+ inputTopic,
+ Serdes.String().serializer(),
+ Serdes.String().serializer());
+
+
+ TestOutputTopic> outputProcessedMapData = driver.createOutputTopic(
+ outputTopic,
+ Serdes.String().deserializer(),
+ JsonSerdes.ProcessedMapGeoJson().deserializer());
+
+ inputProcessedMapData.pipeInput(key, inputProcessedMap1);
+ inputProcessedMapData.pipeInput(key, inputProcessedMap2);
+ inputProcessedMapData.pipeInput(key, inputProcessedMap3);
+ inputProcessedMapData.pipeInput(key, inputProcessedMap4);
+
+ List>> mapDeduplicatorResults = outputProcessedMapData.readKeyValuesToList();
+
+ // validate that only 3 messages make it through
+ assertEquals(3, mapDeduplicatorResults.size());
+
+ ProcessedMap map1 = objectMapper.readValue(inputProcessedMap1, typeReference);
+ ProcessedMap map3 = objectMapper.readValue(inputProcessedMap3, typeReference);
+ ProcessedMap map4 = objectMapper.readValue(inputProcessedMap4, typeReference);
+
+ assertEquals(map1.getProperties().getOdeReceivedAt(), mapDeduplicatorResults.get(0).value.getProperties().getOdeReceivedAt());
+ assertEquals(map3.getProperties().getOdeReceivedAt(), mapDeduplicatorResults.get(1).value.getProperties().getOdeReceivedAt());
+ assertEquals(map4.getProperties().getOdeReceivedAt(), mapDeduplicatorResults.get(2).value.getProperties().getOdeReceivedAt());
+
+ } catch (JsonMappingException e) {
+ e.printStackTrace();
+ } catch (JsonProcessingException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/jpo-deduplicator/jpo-deduplicator/src/test/java/deduplicator/ProcessedMapWktDeduplicatorTopologyTest.java b/jpo-deduplicator/jpo-deduplicator/src/test/java/deduplicator/ProcessedMapWktDeduplicatorTopologyTest.java
new file mode 100644
index 0000000..7b1d2fb
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/test/java/deduplicator/ProcessedMapWktDeduplicatorTopologyTest.java
@@ -0,0 +1,101 @@
+package deduplicator;
+
+import org.apache.kafka.common.serialization.Serdes;
+import org.apache.kafka.streams.KeyValue;
+import org.apache.kafka.streams.TestInputTopic;
+import org.apache.kafka.streams.TestOutputTopic;
+import org.apache.kafka.streams.Topology;
+import org.apache.kafka.streams.TopologyTestDriver;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+
+import us.dot.its.jpo.deduplicator.DeduplicatorProperties;
+import us.dot.its.jpo.deduplicator.deduplicator.topologies.ProcessedMapWktDeduplicatorTopology;
+import us.dot.its.jpo.geojsonconverter.pojos.geojson.map.ProcessedMap;
+import us.dot.its.jpo.geojsonconverter.serialization.JsonSerdes;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.List;
+
+public class ProcessedMapWktDeduplicatorTopologyTest {
+
+ String inputTopic = "topic.ProcessedMapWKT";
+ String outputTopic = "topic.DeduplicatedProcessedMapWKT";
+
+
+ TypeReference> typeReference = new TypeReference<>(){};
+ ObjectMapper objectMapper = new ObjectMapper();
+
+ String inputProcessedMapWkt1 = "{\"mapFeatureCollection\":{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"id\":1,\"geometry\":\"LINESTRING (-105.0907089 39.587905, -105.0906245 39.5876246, -105.0905203 39.587281, -105.0904383 39.5870554, -105.0903588 39.5868383, -105.0902622 39.5865865, -105.0901249 39.5862612, -105.0900451 39.5860819, -105.0899283 39.5858283, -105.0898739 39.5857117, -105.0895814 39.5851569, -105.0888764 39.5839527)\",\"properties\":{\"laneId\":1,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":1,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":15,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":2,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":2,\"geometry\":\"LINESTRING (-105.0907462 39.5878956, -105.090652 39.5875596, -105.090534 39.5871793, -105.0903457 39.5866864, -105.0902123 39.5863581, -105.0900802 39.5860572, -105.0898164 39.5855019, -105.0895409 39.5849856, -105.088922 39.5839259)\",\"properties\":{\"laneId\":2,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":1,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":14,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":2,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":3,\"geometry\":\"LINESTRING (-105.0907914 39.5878879, -105.090747 39.5877247, -105.0906498 39.5874141, -105.0906262 39.5873356, -105.0905865 39.5872922)\",\"properties\":{\"laneId\":3,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":1,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":10,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":2,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":6,\"geometry\":\"LINESTRING (-105.0910008 39.5878477, -105.0909927 39.5878181)\",\"properties\":{\"laneId\":6,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":2,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":5,\"geometry\":\"LINESTRING (-105.090959 39.5878557, -105.0909501 39.5878218)\",\"properties\":{\"laneId\":5,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":2,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":4,\"geometry\":\"LINESTRING (-105.090914 39.5878612, -105.0909051 39.5878298)\",\"properties\":{\"laneId\":4,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":2,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":10,\"geometry\":\"LINESTRING (-105.0911626 39.5880622, -105.0912043 39.5880536)\",\"properties\":{\"laneId\":10,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":4,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":8,\"geometry\":\"LINESTRING (-105.0911477 39.587995, -105.0914565 39.5879427, -105.0917937 39.5879029, -105.0922121 39.5878724, -105.0926509 39.5878748, -105.0930303 39.5879073, -105.0932697 39.5879503, -105.0937243 39.5880569, -105.0940309 39.5881258, -105.0943257 39.5881804, -105.094592 39.5882097)\",\"properties\":{\"laneId\":8,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":3,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":15,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":4,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":7,\"geometry\":\"LINESTRING (-105.0911442 39.5879589, -105.0914154 39.5879165, -105.0916346 39.5878851, -105.0918433 39.5878639, -105.0921546 39.5878547)\",\"properties\":{\"laneId\":7,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":3,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":6,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":9,\"geometry\":\"LINESTRING (-105.0911534 39.5880261, -105.091436 39.5879812, -105.0916658 39.5879507, -105.091881 39.5879277, -105.0921287 39.5878972)\",\"properties\":{\"laneId\":9,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":3,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":14,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":4,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":12,\"geometry\":\"LINESTRING (-105.0910447 39.5881948, -105.0911481 39.5886317, -105.091196 39.588862, -105.0912349 39.5890282, -105.0912722 39.5893202, -105.0913306 39.5897261, -105.0913695 39.5900324, -105.0914008 39.5903008, -105.0914893 39.5913099, -105.091527 39.5923157)\",\"properties\":{\"laneId\":12,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":5,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":5,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":6,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":13,\"geometry\":\"LINESTRING (-105.0910013 39.5881975, -105.0911059 39.5886309, -105.091144 39.5888313, -105.0911829 39.5890442, -105.0912308 39.5893169, -105.0912689 39.5895877, -105.0913005 39.5898143, -105.0913313 39.5900714, -105.0913597 39.5902968, -105.0914461 39.5913017, -105.0914756 39.592324)\",\"properties\":{\"laneId\":13,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":5,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":4,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":6,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":11,\"geometry\":\"LINESTRING (-105.0910891 39.5881859, -105.0911549 39.5884681, -105.091196 39.5886783, -105.091222 39.5888049, -105.0912401 39.5889649)\",\"properties\":{\"laneId\":11,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":5,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":10,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":14,\"geometry\":\"LINESTRING (-105.0908389 39.5882151, -105.0908478 39.5882471)\",\"properties\":{\"laneId\":14,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":6,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":15,\"geometry\":\"LINESTRING (-105.0907875 39.58822, -105.0907979 39.5882514)\",\"properties\":{\"laneId\":15,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":6,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}}]},\"connectingLanesFeatureCollection\":{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"id\":\"1-15\",\"geometry\":\"LINESTRING (-105.0907089 39.587905, -105.0907875 39.58822)\",\"properties\":{\"signalGroupId\":2,\"ingressLaneId\":1,\"egressLaneId\":15}},{\"type\":\"Feature\",\"id\":\"2-14\",\"geometry\":\"LINESTRING (-105.0907462 39.5878956, -105.0908389 39.5882151)\",\"properties\":{\"signalGroupId\":2,\"ingressLaneId\":2,\"egressLaneId\":14}},{\"type\":\"Feature\",\"id\":\"3-10\",\"geometry\":\"LINESTRING (-105.0907914 39.5878879, -105.0911626 39.5880622)\",\"properties\":{\"signalGroupId\":2,\"ingressLaneId\":3,\"egressLaneId\":10}},{\"type\":\"Feature\",\"id\":\"8-15\",\"geometry\":\"LINESTRING (-105.0911477 39.587995, -105.0907875 39.58822)\",\"properties\":{\"signalGroupId\":4,\"ingressLaneId\":8,\"egressLaneId\":15}},{\"type\":\"Feature\",\"id\":\"7-6\",\"geometry\":\"LINESTRING (-105.0911442 39.5879589, -105.0910008 39.5878477)\",\"properties\":{\"ingressLaneId\":7,\"egressLaneId\":6}},{\"type\":\"Feature\",\"id\":\"9-14\",\"geometry\":\"LINESTRING (-105.0911534 39.5880261, -105.0908389 39.5882151)\",\"properties\":{\"signalGroupId\":4,\"ingressLaneId\":9,\"egressLaneId\":14}},{\"type\":\"Feature\",\"id\":\"12-5\",\"geometry\":\"LINESTRING (-105.0910447 39.5881948, -105.090959 39.5878557)\",\"properties\":{\"signalGroupId\":6,\"ingressLaneId\":12,\"egressLaneId\":5}},{\"type\":\"Feature\",\"id\":\"13-4\",\"geometry\":\"LINESTRING (-105.0910013 39.5881975, -105.090914 39.5878612)\",\"properties\":{\"signalGroupId\":6,\"ingressLaneId\":13,\"egressLaneId\":4}},{\"type\":\"Feature\",\"id\":\"11-10\",\"geometry\":\"LINESTRING (-105.0910891 39.5881859, -105.0911626 39.5880622)\",\"properties\":{\"ingressLaneId\":11,\"egressLaneId\":10}}]},\"properties\":{\"messageType\":\"MAP\",\"odeReceivedAt\":\"2024-05-06T21:35:21.713Z\",\"originIp\":\"10.11.81.12\",\"intersectionId\":12109,\"msgIssueRevision\":2,\"revision\":2,\"refPoint\":{\"latitude\":39.5880413,\"longitude\":-105.0908854,\"elevation\":1500},\"cti4501Conformant\":false,\"laneWidth\":366,\"mapSource\":\"RSU\",\"timeStamp\":\"2024-05-06T21:35:21.713Z\"}}";
+
+
+ //String inputProcessedMapWkt1 = "{\"mapFeatureCollection\":{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"id\":1,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907089,39.587905],[-105.0906245,39.5876246],[-105.0905203,39.587281],[-105.0904383,39.5870554],[-105.0903588,39.5868383],[-105.0902622,39.5865865],[-105.0901249,39.5862612],[-105.0900451,39.5860819],[-105.0899283,39.5858283],[-105.0898739,39.5857117],[-105.0895814,39.5851569],[-105.0888764,39.5839527]]},\"properties\":{\"nodes\":[{\"delta\":[1511,-1514]},{\"delta\":[723,-3116],\"delevation\":10},{\"delta\":[892,-3818],\"delevation\":20},{\"delta\":[702,-2507],\"delevation\":20},{\"delta\":[681,-2412],\"delevation\":10},{\"delta\":[827,-2798],\"delevation\":10},{\"delta\":[1176,-3614],\"delevation\":20},{\"delta\":[683,-1992]},{\"delta\":[1000,-2818],\"delevation\":10},{\"delta\":[466,-1295],\"delevation\":20},{\"delta\":[2505,-6164],\"delevation\":20},{\"delta\":[6037,-13380],\"delevation\":70}],\"laneId\":1,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":1,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":15,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":2,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":2,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907462,39.5878956],[-105.090652,39.5875596],[-105.090534,39.5871793],[-105.0903457,39.5866864],[-105.0902123,39.5863581],[-105.0900802,39.5860572],[-105.0898164,39.5855019],[-105.0895409,39.5849856],[-105.088922,39.5839259]]},\"properties\":{\"nodes\":[{\"delta\":[1192,-1619]},{\"delta\":[807,-3733],\"delevation\":30},{\"delta\":[1010,-4226],\"delevation\":10},{\"delta\":[1612,-5477],\"delevation\":30},{\"delta\":[1142,-3648],\"delevation\":20},{\"delta\":[1131,-3343],\"delevation\":10},{\"delta\":[2259,-6170],\"delevation\":30},{\"delta\":[2359,-5737],\"delevation\":30},{\"delta\":[5300,-11774],\"delevation\":50}],\"laneId\":2,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":1,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":14,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":2,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":3,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907914,39.5878879],[-105.090747,39.5877247],[-105.0906498,39.5874141],[-105.0906262,39.5873356],[-105.0905865,39.5872922]]},\"properties\":{\"nodes\":[{\"delta\":[805,-1704],\"delevation\":10},{\"delta\":[380,-1813]},{\"delta\":[832,-3451],\"delevation\":30},{\"delta\":[202,-872]},{\"delta\":[340,-482],\"delevation\":-10}],\"laneId\":3,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":1,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":10,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":2,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":6,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910008,39.5878477],[-105.0909927,39.5878181]]},\"properties\":{\"nodes\":[{\"delta\":[-988,-2151],\"delevation\":20},{\"delta\":[69,-329]}],\"laneId\":6,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":2,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":5,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.090959,39.5878557],[-105.0909501,39.5878218]]},\"properties\":{\"nodes\":[{\"delta\":[-630,-2062],\"delevation\":10},{\"delta\":[76,-377],\"delevation\":10}],\"laneId\":5,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":2,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":4,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.090914,39.5878612],[-105.0909051,39.5878298]]},\"properties\":{\"nodes\":[{\"delta\":[-245,-2001],\"delevation\":10},{\"delta\":[76,-349]}],\"laneId\":4,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":2,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":10,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911626,39.5880622],[-105.0912043,39.5880536]]},\"properties\":{\"nodes\":[{\"delta\":[-2374,232],\"delevation\":10},{\"delta\":[-357,-96]}],\"laneId\":10,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":4,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":8,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911477,39.587995],[-105.0914565,39.5879427],[-105.0917937,39.5879029],[-105.0922121,39.5878724],[-105.0926509,39.5878748],[-105.0930303,39.5879073],[-105.0932697,39.5879503],[-105.0937243,39.5880569],[-105.0940309,39.5881258],[-105.0943257,39.5881804],[-105.094592,39.5882097]]},\"properties\":{\"nodes\":[{\"delta\":[-2246,-514],\"delevation\":10},{\"delta\":[-2644,-581]},{\"delta\":[-2887,-442],\"delevation\":10},{\"delta\":[-3583,-339],\"delevation\":10},{\"delta\":[-3757,27]},{\"delta\":[-3249,361],\"delevation\":-10},{\"delta\":[-2050,478]},{\"delta\":[-3893,1184]},{\"delta\":[-2625,766],\"delevation\":-10},{\"delta\":[-2524,607],\"delevation\":10},{\"delta\":[-2280,325],\"delevation\":10}],\"laneId\":8,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":3,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":15,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":4,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":7,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911442,39.5879589],[-105.0914154,39.5879165],[-105.0916346,39.5878851],[-105.0918433,39.5878639],[-105.0921546,39.5878547]]},\"properties\":{\"nodes\":[{\"delta\":[-2216,-915],\"delevation\":10},{\"delta\":[-2322,-471]},{\"delta\":[-1877,-349],\"delevation\":10},{\"delta\":[-1787,-235]},{\"delta\":[-2666,-102],\"delevation\":10}],\"laneId\":7,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":3,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":6,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":9,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911534,39.5880261],[-105.091436,39.5879812],[-105.0916658,39.5879507],[-105.091881,39.5879277],[-105.0921287,39.5878972]]},\"properties\":{\"nodes\":[{\"delta\":[-2295,-169],\"delevation\":10},{\"delta\":[-2420,-499]},{\"delta\":[-1968,-339],\"delevation\":10},{\"delta\":[-1843,-256]},{\"delta\":[-2121,-339]}],\"laneId\":9,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":3,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":14,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":4,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":12,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910447,39.5881948],[-105.0911481,39.5886317],[-105.091196,39.588862],[-105.0912349,39.5890282],[-105.0912722,39.5893202],[-105.0913306,39.5897261],[-105.0913695,39.5900324],[-105.0914008,39.5903008],[-105.0914893,39.5913099],[-105.091527,39.5923157]]},\"properties\":{\"nodes\":[{\"delta\":[-1364,1705],\"delevation\":10},{\"delta\":[-885,4854],\"delevation\":-30},{\"delta\":[-410,2559],\"delevation\":10},{\"delta\":[-333,1847],\"delevation\":-10},{\"delta\":[-319,3244],\"delevation\":-20},{\"delta\":[-500,4510]},{\"delta\":[-333,3403],\"delevation\":-30},{\"delta\":[-268,2982]},{\"delta\":[-758,11212],\"delevation\":-30},{\"delta\":[-323,11176],\"delevation\":-70}],\"laneId\":12,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":5,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":5,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":6,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":13,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910013,39.5881975],[-105.0911059,39.5886309],[-105.091144,39.5888313],[-105.0911829,39.5890442],[-105.0912308,39.5893169],[-105.0912689,39.5895877],[-105.0913005,39.5898143],[-105.0913313,39.5900714],[-105.0913597,39.5902968],[-105.0914461,39.5913017],[-105.0914756,39.592324]]},\"properties\":{\"nodes\":[{\"delta\":[-992,1735],\"delevation\":10},{\"delta\":[-896,4816],\"delevation\":-30},{\"delta\":[-326,2227],\"delevation\":10},{\"delta\":[-333,2366]},{\"delta\":[-410,3030],\"delevation\":-20},{\"delta\":[-326,3009],\"delevation\":-10},{\"delta\":[-271,2518],\"delevation\":-10},{\"delta\":[-264,2857],\"delevation\":-20},{\"delta\":[-243,2504]},{\"delta\":[-740,11165],\"delevation\":-30},{\"delta\":[-253,11359],\"delevation\":-70}],\"laneId\":13,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":5,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":4,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":6,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":11,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910891,39.5881859],[-105.0911549,39.5884681],[-105.091196,39.5886783],[-105.091222,39.5888049],[-105.0912401,39.5889649]]},\"properties\":{\"nodes\":[{\"delta\":[-1744,1607],\"delevation\":10},{\"delta\":[-563,3136],\"delevation\":-20},{\"delta\":[-352,2336],\"delevation\":-10},{\"delta\":[-223,1407],\"delevation\":10},{\"delta\":[-155,1778]}],\"laneId\":11,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":5,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":10,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":14,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0908389,39.5882151],[-105.0908478,39.5882471]]},\"properties\":{\"nodes\":[{\"delta\":[398,1931],\"delevation\":-10},{\"delta\":[-76,356]}],\"laneId\":14,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":6,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":15,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907875,39.58822],[-105.0907979,39.5882514]]},\"properties\":{\"nodes\":[{\"delta\":[838,1985],\"delevation\":-20},{\"delta\":[-89,349]}],\"laneId\":15,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":6,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}}]},\"connectingLanesFeatureCollection\":{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"id\":\"1-15\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907089,39.587905],[-105.0907875,39.58822]]},\"properties\":{\"signalGroupId\":2,\"ingressLaneId\":1,\"egressLaneId\":15}},{\"type\":\"Feature\",\"id\":\"2-14\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907462,39.5878956],[-105.0908389,39.5882151]]},\"properties\":{\"signalGroupId\":2,\"ingressLaneId\":2,\"egressLaneId\":14}},{\"type\":\"Feature\",\"id\":\"3-10\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0907914,39.5878879],[-105.0911626,39.5880622]]},\"properties\":{\"signalGroupId\":2,\"ingressLaneId\":3,\"egressLaneId\":10}},{\"type\":\"Feature\",\"id\":\"8-15\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911477,39.587995],[-105.0907875,39.58822]]},\"properties\":{\"signalGroupId\":4,\"ingressLaneId\":8,\"egressLaneId\":15}},{\"type\":\"Feature\",\"id\":\"7-6\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911442,39.5879589],[-105.0910008,39.5878477]]},\"properties\":{\"ingressLaneId\":7,\"egressLaneId\":6}},{\"type\":\"Feature\",\"id\":\"9-14\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911534,39.5880261],[-105.0908389,39.5882151]]},\"properties\":{\"signalGroupId\":4,\"ingressLaneId\":9,\"egressLaneId\":14}},{\"type\":\"Feature\",\"id\":\"12-5\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910447,39.5881948],[-105.090959,39.5878557]]},\"properties\":{\"signalGroupId\":6,\"ingressLaneId\":12,\"egressLaneId\":5}},{\"type\":\"Feature\",\"id\":\"13-4\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910013,39.5881975],[-105.090914,39.5878612]]},\"properties\":{\"signalGroupId\":6,\"ingressLaneId\":13,\"egressLaneId\":4}},{\"type\":\"Feature\",\"id\":\"11-10\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910891,39.5881859],[-105.0911626,39.5880622]]},\"properties\":{\"ingressLaneId\":11,\"egressLaneId\":10}}]},\"properties\":{\"messageType\":\"MAP\",\"odeReceivedAt\":\"2024-02-22T23:26:26.114Z\",\"originIp\":\"10.11.81.12\",\"intersectionId\":12109,\"msgIssueRevision\":2,\"revision\":2,\"refPoint\":{\"latitude\":39.5880413,\"longitude\":-105.0908854,\"elevation\":1691},\"cti4501Conformant\":false,\"validationMessages\":[{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].id.region: null found, integer expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].id.region\",\"schemaPath\":\"#/$defs/J2735RoadRegulatorID/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[3].connectsTo: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[3].connectsTo\",\"schemaPath\":\"#/$defs/J2735ConnectsToList_Wrapper/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[3].maneuvers: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[3].maneuvers\",\"schemaPath\":\"#/$defs/J2735AllowedManeuvers/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].connectsTo: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].connectsTo\",\"schemaPath\":\"#/$defs/J2735ConnectsToList_Wrapper/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].maneuvers: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].maneuvers\",\"schemaPath\":\"#/$defs/J2735AllowedManeuvers/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].connectsTo: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].connectsTo\",\"schemaPath\":\"#/$defs/J2735ConnectsToList_Wrapper/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].maneuvers: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].maneuvers\",\"schemaPath\":\"#/$defs/J2735AllowedManeuvers/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].connectsTo: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].connectsTo\",\"schemaPath\":\"#/$defs/J2735ConnectsToList_Wrapper/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].maneuvers: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].maneuvers\",\"schemaPath\":\"#/$defs/J2735AllowedManeuvers/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[8].connectsTo.connectsTo[0].signalGroup: null found, integer expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[8].connectsTo.connectsTo[0].signalGroup\",\"schemaPath\":\"#/$defs/J2735SignalGroupID/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[12].connectsTo.connectsTo[0].signalGroup: null found, integer expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[12].connectsTo.connectsTo[0].signalGroup\",\"schemaPath\":\"#/$defs/J2735SignalGroupID/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[13].connectsTo: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[13].connectsTo\",\"schemaPath\":\"#/$defs/J2735ConnectsToList_Wrapper/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[13].maneuvers: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[13].maneuvers\",\"schemaPath\":\"#/$defs/J2735AllowedManeuvers/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[14].connectsTo: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[14].connectsTo\",\"schemaPath\":\"#/$defs/J2735ConnectsToList_Wrapper/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[14].maneuvers: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[14].maneuvers\",\"schemaPath\":\"#/$defs/J2735AllowedManeuvers/type\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].speedLimits: null found, object expected\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].speedLimits\",\"schemaPath\":\"#/$defs/J2735SpeedLimitList_Wrapper/type\"}],\"laneWidth\":366,\"mapSource\":\"RSU\",\"timeStamp\":\"2024-02-22T23:26:26.114Z\"}}";
+ String inputProcessedMapWkt2 = "{\"mapFeatureCollection\":{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"id\":1,\"geometry\":\"LINESTRING (-105.0907089 39.587905, -105.0906245 39.5876246, -105.0905203 39.587281, -105.0904383 39.5870554, -105.0903588 39.5868383, -105.0902622 39.5865865, -105.0901249 39.5862612, -105.0900451 39.5860819, -105.0899283 39.5858283, -105.0898739 39.5857117, -105.0895814 39.5851569, -105.0888764 39.5839527)\",\"properties\":{\"laneId\":1,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":1,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":15,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":2,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":2,\"geometry\":\"LINESTRING (-105.0907462 39.5878956, -105.090652 39.5875596, -105.090534 39.5871793, -105.0903457 39.5866864, -105.0902123 39.5863581, -105.0900802 39.5860572, -105.0898164 39.5855019, -105.0895409 39.5849856, -105.088922 39.5839259)\",\"properties\":{\"laneId\":2,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":1,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":14,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":2,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":3,\"geometry\":\"LINESTRING (-105.0907914 39.5878879, -105.090747 39.5877247, -105.0906498 39.5874141, -105.0906262 39.5873356, -105.0905865 39.5872922)\",\"properties\":{\"laneId\":3,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":1,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":10,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":2,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":6,\"geometry\":\"LINESTRING (-105.0910008 39.5878477, -105.0909927 39.5878181)\",\"properties\":{\"laneId\":6,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":2,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":5,\"geometry\":\"LINESTRING (-105.090959 39.5878557, -105.0909501 39.5878218)\",\"properties\":{\"laneId\":5,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":2,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":4,\"geometry\":\"LINESTRING (-105.090914 39.5878612, -105.0909051 39.5878298)\",\"properties\":{\"laneId\":4,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":2,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":10,\"geometry\":\"LINESTRING (-105.0911626 39.5880622, -105.0912043 39.5880536)\",\"properties\":{\"laneId\":10,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":4,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":8,\"geometry\":\"LINESTRING (-105.0911477 39.587995, -105.0914565 39.5879427, -105.0917937 39.5879029, -105.0922121 39.5878724, -105.0926509 39.5878748, -105.0930303 39.5879073, -105.0932697 39.5879503, -105.0937243 39.5880569, -105.0940309 39.5881258, -105.0943257 39.5881804, -105.094592 39.5882097)\",\"properties\":{\"laneId\":8,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":3,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":15,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":4,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":7,\"geometry\":\"LINESTRING (-105.0911442 39.5879589, -105.0914154 39.5879165, -105.0916346 39.5878851, -105.0918433 39.5878639, -105.0921546 39.5878547)\",\"properties\":{\"laneId\":7,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":3,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":6,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":9,\"geometry\":\"LINESTRING (-105.0911534 39.5880261, -105.091436 39.5879812, -105.0916658 39.5879507, -105.091881 39.5879277, -105.0921287 39.5878972)\",\"properties\":{\"laneId\":9,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":3,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":14,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":4,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":12,\"geometry\":\"LINESTRING (-105.0910447 39.5881948, -105.0911481 39.5886317, -105.091196 39.588862, -105.0912349 39.5890282, -105.0912722 39.5893202, -105.0913306 39.5897261, -105.0913695 39.5900324, -105.0914008 39.5903008, -105.0914893 39.5913099, -105.091527 39.5923157)\",\"properties\":{\"laneId\":12,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":5,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":5,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":6,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":13,\"geometry\":\"LINESTRING (-105.0910013 39.5881975, -105.0911059 39.5886309, -105.091144 39.5888313, -105.0911829 39.5890442, -105.0912308 39.5893169, -105.0912689 39.5895877, -105.0913005 39.5898143, -105.0913313 39.5900714, -105.0913597 39.5902968, -105.0914461 39.5913017, -105.0914756 39.592324)\",\"properties\":{\"laneId\":13,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":5,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":4,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":6,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":11,\"geometry\":\"LINESTRING (-105.0910891 39.5881859, -105.0911549 39.5884681, -105.091196 39.5886783, -105.091222 39.5888049, -105.0912401 39.5889649)\",\"properties\":{\"laneId\":11,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":5,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":10,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":14,\"geometry\":\"LINESTRING (-105.0908389 39.5882151, -105.0908478 39.5882471)\",\"properties\":{\"laneId\":14,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":6,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":15,\"geometry\":\"LINESTRING (-105.0907875 39.58822, -105.0907979 39.5882514)\",\"properties\":{\"laneId\":15,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":6,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}}]},\"connectingLanesFeatureCollection\":{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"id\":\"1-15\",\"geometry\":\"LINESTRING (-105.0907089 39.587905, -105.0907875 39.58822)\",\"properties\":{\"signalGroupId\":2,\"ingressLaneId\":1,\"egressLaneId\":15}},{\"type\":\"Feature\",\"id\":\"2-14\",\"geometry\":\"LINESTRING (-105.0907462 39.5878956, -105.0908389 39.5882151)\",\"properties\":{\"signalGroupId\":2,\"ingressLaneId\":2,\"egressLaneId\":14}},{\"type\":\"Feature\",\"id\":\"3-10\",\"geometry\":\"LINESTRING (-105.0907914 39.5878879, -105.0911626 39.5880622)\",\"properties\":{\"signalGroupId\":2,\"ingressLaneId\":3,\"egressLaneId\":10}},{\"type\":\"Feature\",\"id\":\"8-15\",\"geometry\":\"LINESTRING (-105.0911477 39.587995, -105.0907875 39.58822)\",\"properties\":{\"signalGroupId\":4,\"ingressLaneId\":8,\"egressLaneId\":15}},{\"type\":\"Feature\",\"id\":\"7-6\",\"geometry\":\"LINESTRING (-105.0911442 39.5879589, -105.0910008 39.5878477)\",\"properties\":{\"ingressLaneId\":7,\"egressLaneId\":6}},{\"type\":\"Feature\",\"id\":\"9-14\",\"geometry\":\"LINESTRING (-105.0911534 39.5880261, -105.0908389 39.5882151)\",\"properties\":{\"signalGroupId\":4,\"ingressLaneId\":9,\"egressLaneId\":14}},{\"type\":\"Feature\",\"id\":\"12-5\",\"geometry\":\"LINESTRING (-105.0910447 39.5881948, -105.090959 39.5878557)\",\"properties\":{\"signalGroupId\":6,\"ingressLaneId\":12,\"egressLaneId\":5}},{\"type\":\"Feature\",\"id\":\"13-4\",\"geometry\":\"LINESTRING (-105.0910013 39.5881975, -105.090914 39.5878612)\",\"properties\":{\"signalGroupId\":6,\"ingressLaneId\":13,\"egressLaneId\":4}},{\"type\":\"Feature\",\"id\":\"11-10\",\"geometry\":\"LINESTRING (-105.0910891 39.5881859, -105.0911626 39.5880622)\",\"properties\":{\"ingressLaneId\":11,\"egressLaneId\":10}}]},\"properties\":{\"messageType\":\"MAP\",\"odeReceivedAt\":\"2024-05-03T20:58:48.748Z\",\"originIp\":\"10.11.81.12\",\"intersectionId\":12109,\"msgIssueRevision\":2,\"revision\":2,\"refPoint\":{\"latitude\":39.5880413,\"longitude\":-105.0908854,\"elevation\":1691},\"cti4501Conformant\":false,\"laneWidth\":366,\"mapSource\":\"RSU\",\"timeStamp\":\"2024-05-03T20:58:48.748Z\"}}";
+ String inputProcessedMapWkt3 = "{\"mapFeatureCollection\":{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"id\":1,\"geometry\":\"LINESTRING (-105.0907089 39.587905, -105.0906245 39.5876246, -105.0905203 39.587281, -105.0904383 39.5870554, -105.0903588 39.5868383, -105.0902622 39.5865865, -105.0901249 39.5862612, -105.0900451 39.5860819, -105.0899283 39.5858283, -105.0898739 39.5857117, -105.0895814 39.5851569, -105.0888764 39.5839527)\",\"properties\":{\"laneId\":1,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":1,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":15,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":2,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":2,\"geometry\":\"LINESTRING (-105.0907462 39.5878956, -105.090652 39.5875596, -105.090534 39.5871793, -105.0903457 39.5866864, -105.0902123 39.5863581, -105.0900802 39.5860572, -105.0898164 39.5855019, -105.0895409 39.5849856, -105.088922 39.5839259)\",\"properties\":{\"laneId\":2,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":1,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":14,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":2,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":3,\"geometry\":\"LINESTRING (-105.0907914 39.5878879, -105.090747 39.5877247, -105.0906498 39.5874141, -105.0906262 39.5873356, -105.0905865 39.5872922)\",\"properties\":{\"laneId\":3,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":1,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":10,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":2,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":6,\"geometry\":\"LINESTRING (-105.0910008 39.5878477, -105.0909927 39.5878181)\",\"properties\":{\"laneId\":6,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":2,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":5,\"geometry\":\"LINESTRING (-105.090959 39.5878557, -105.0909501 39.5878218)\",\"properties\":{\"laneId\":5,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":2,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":4,\"geometry\":\"LINESTRING (-105.090914 39.5878612, -105.0909051 39.5878298)\",\"properties\":{\"laneId\":4,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":2,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":10,\"geometry\":\"LINESTRING (-105.0911626 39.5880622, -105.0912043 39.5880536)\",\"properties\":{\"laneId\":10,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":4,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":8,\"geometry\":\"LINESTRING (-105.0911477 39.587995, -105.0914565 39.5879427, -105.0917937 39.5879029, -105.0922121 39.5878724, -105.0926509 39.5878748, -105.0930303 39.5879073, -105.0932697 39.5879503, -105.0937243 39.5880569, -105.0940309 39.5881258, -105.0943257 39.5881804, -105.094592 39.5882097)\",\"properties\":{\"laneId\":8,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":3,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":15,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":4,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":7,\"geometry\":\"LINESTRING (-105.0911442 39.5879589, -105.0914154 39.5879165, -105.0916346 39.5878851, -105.0918433 39.5878639, -105.0921546 39.5878547)\",\"properties\":{\"laneId\":7,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":3,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":6,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":9,\"geometry\":\"LINESTRING (-105.0911534 39.5880261, -105.091436 39.5879812, -105.0916658 39.5879507, -105.091881 39.5879277, -105.0921287 39.5878972)\",\"properties\":{\"laneId\":9,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":3,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":14,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":4,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":12,\"geometry\":\"LINESTRING (-105.0910447 39.5881948, -105.0911481 39.5886317, -105.091196 39.588862, -105.0912349 39.5890282, -105.0912722 39.5893202, -105.0913306 39.5897261, -105.0913695 39.5900324, -105.0914008 39.5903008, -105.0914893 39.5913099, -105.091527 39.5923157)\",\"properties\":{\"laneId\":12,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":5,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":5,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":6,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":13,\"geometry\":\"LINESTRING (-105.0910013 39.5881975, -105.0911059 39.5886309, -105.091144 39.5888313, -105.0911829 39.5890442, -105.0912308 39.5893169, -105.0912689 39.5895877, -105.0913005 39.5898143, -105.0913313 39.5900714, -105.0913597 39.5902968, -105.0914461 39.5913017, -105.0914756 39.592324)\",\"properties\":{\"laneId\":13,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":5,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":4,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":6,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":11,\"geometry\":\"LINESTRING (-105.0910891 39.5881859, -105.0911549 39.5884681, -105.091196 39.5886783, -105.091222 39.5888049, -105.0912401 39.5889649)\",\"properties\":{\"laneId\":11,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":5,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":10,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":14,\"geometry\":\"LINESTRING (-105.0908389 39.5882151, -105.0908478 39.5882471)\",\"properties\":{\"laneId\":14,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":6,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":15,\"geometry\":\"LINESTRING (-105.0907875 39.58822, -105.0907979 39.5882514)\",\"properties\":{\"laneId\":15,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":6,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}}]},\"connectingLanesFeatureCollection\":{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"id\":\"1-15\",\"geometry\":\"LINESTRING (-105.0907089 39.587905, -105.0907875 39.58822)\",\"properties\":{\"signalGroupId\":2,\"ingressLaneId\":1,\"egressLaneId\":15}},{\"type\":\"Feature\",\"id\":\"2-14\",\"geometry\":\"LINESTRING (-105.0907462 39.5878956, -105.0908389 39.5882151)\",\"properties\":{\"signalGroupId\":2,\"ingressLaneId\":2,\"egressLaneId\":14}},{\"type\":\"Feature\",\"id\":\"3-10\",\"geometry\":\"LINESTRING (-105.0907914 39.5878879, -105.0911626 39.5880622)\",\"properties\":{\"signalGroupId\":2,\"ingressLaneId\":3,\"egressLaneId\":10}},{\"type\":\"Feature\",\"id\":\"8-15\",\"geometry\":\"LINESTRING (-105.0911477 39.587995, -105.0907875 39.58822)\",\"properties\":{\"signalGroupId\":4,\"ingressLaneId\":8,\"egressLaneId\":15}},{\"type\":\"Feature\",\"id\":\"7-6\",\"geometry\":\"LINESTRING (-105.0911442 39.5879589, -105.0910008 39.5878477)\",\"properties\":{\"ingressLaneId\":7,\"egressLaneId\":6}},{\"type\":\"Feature\",\"id\":\"9-14\",\"geometry\":\"LINESTRING (-105.0911534 39.5880261, -105.0908389 39.5882151)\",\"properties\":{\"signalGroupId\":4,\"ingressLaneId\":9,\"egressLaneId\":14}},{\"type\":\"Feature\",\"id\":\"12-5\",\"geometry\":\"LINESTRING (-105.0910447 39.5881948, -105.090959 39.5878557)\",\"properties\":{\"signalGroupId\":6,\"ingressLaneId\":12,\"egressLaneId\":5}},{\"type\":\"Feature\",\"id\":\"13-4\",\"geometry\":\"LINESTRING (-105.0910013 39.5881975, -105.090914 39.5878612)\",\"properties\":{\"signalGroupId\":6,\"ingressLaneId\":13,\"egressLaneId\":4}},{\"type\":\"Feature\",\"id\":\"11-10\",\"geometry\":\"LINESTRING (-105.0910891 39.5881859, -105.0911626 39.5880622)\",\"properties\":{\"ingressLaneId\":11,\"egressLaneId\":10}}]},\"properties\":{\"messageType\":\"MAP\",\"odeReceivedAt\":\"2024-05-03T20:58:48.748Z\",\"originIp\":\"10.11.81.12\",\"intersectionId\":12109,\"msgIssueRevision\":2,\"revision\":2,\"refPoint\":{\"latitude\":39.5880413,\"longitude\":-105.0908854,\"elevation\":1691},\"cti4501Conformant\":false,\"laneWidth\":366,\"mapSource\":\"RSU\",\"timeStamp\":\"2024-05-03T20:59:48.748Z\"}}";
+ String inputProcessedMapWkt4 = "{\"mapFeatureCollection\":{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"id\":1,\"geometry\":\"LINESTRING (-105.0907089 39.587905, -105.0906245 39.5876246, -105.0905203 39.587281, -105.0904383 39.5870554, -105.0903588 39.5868383, -105.0902622 39.5865865, -105.0901249 39.5862612, -105.0900451 39.5860819, -105.0899283 39.5858283, -105.0898739 39.5857117, -105.0895814 39.5851569, -105.0888764 39.5839527)\",\"properties\":{\"laneId\":1,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":1,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":15,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":2,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":2,\"geometry\":\"LINESTRING (-105.0907462 39.5878956, -105.090652 39.5875596, -105.090534 39.5871793, -105.0903457 39.5866864, -105.0902123 39.5863581, -105.0900802 39.5860572, -105.0898164 39.5855019, -105.0895409 39.5849856, -105.088922 39.5839259)\",\"properties\":{\"laneId\":2,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":1,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":14,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":2,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":3,\"geometry\":\"LINESTRING (-105.0907914 39.5878879, -105.090747 39.5877247, -105.0906498 39.5874141, -105.0906262 39.5873356, -105.0905865 39.5872922)\",\"properties\":{\"laneId\":3,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":1,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":10,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":2,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":6,\"geometry\":\"LINESTRING (-105.0910008 39.5878477, -105.0909927 39.5878181)\",\"properties\":{\"laneId\":6,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":2,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":5,\"geometry\":\"LINESTRING (-105.090959 39.5878557, -105.0909501 39.5878218)\",\"properties\":{\"laneId\":5,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":2,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":4,\"geometry\":\"LINESTRING (-105.090914 39.5878612, -105.0909051 39.5878298)\",\"properties\":{\"laneId\":4,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":2,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":10,\"geometry\":\"LINESTRING (-105.0911626 39.5880622, -105.0912043 39.5880536)\",\"properties\":{\"laneId\":10,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":4,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":8,\"geometry\":\"LINESTRING (-105.0911477 39.587995, -105.0914565 39.5879427, -105.0917937 39.5879029, -105.0922121 39.5878724, -105.0926509 39.5878748, -105.0930303 39.5879073, -105.0932697 39.5879503, -105.0937243 39.5880569, -105.0940309 39.5881258, -105.0943257 39.5881804, -105.094592 39.5882097)\",\"properties\":{\"laneId\":8,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":3,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":15,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":4,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":7,\"geometry\":\"LINESTRING (-105.0911442 39.5879589, -105.0914154 39.5879165, -105.0916346 39.5878851, -105.0918433 39.5878639, -105.0921546 39.5878547)\",\"properties\":{\"laneId\":7,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":3,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":6,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":9,\"geometry\":\"LINESTRING (-105.0911534 39.5880261, -105.091436 39.5879812, -105.0916658 39.5879507, -105.091881 39.5879277, -105.0921287 39.5878972)\",\"properties\":{\"laneId\":9,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":3,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":14,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":4,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":12,\"geometry\":\"LINESTRING (-105.0910447 39.5881948, -105.0911481 39.5886317, -105.091196 39.588862, -105.0912349 39.5890282, -105.0912722 39.5893202, -105.0913306 39.5897261, -105.0913695 39.5900324, -105.0914008 39.5903008, -105.0914893 39.5913099, -105.091527 39.5923157)\",\"properties\":{\"laneId\":12,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":5,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":5,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":6,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":13,\"geometry\":\"LINESTRING (-105.0910013 39.5881975, -105.0911059 39.5886309, -105.091144 39.5888313, -105.0911829 39.5890442, -105.0912308 39.5893169, -105.0912689 39.5895877, -105.0913005 39.5898143, -105.0913313 39.5900714, -105.0913597 39.5902968, -105.0914461 39.5913017, -105.0914756 39.592324)\",\"properties\":{\"laneId\":13,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":5,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":4,\"maneuver\":{\"maneuverStraightAllowed\":true,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"signalGroup\":6,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":11,\"geometry\":\"LINESTRING (-105.0910891 39.5881859, -105.0911549 39.5884681, -105.091196 39.5886783, -105.091222 39.5888049, -105.0912401 39.5889649)\",\"properties\":{\"laneId\":11,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":0,\"ingressApproach\":5,\"ingressPath\":true,\"egressPath\":false,\"maneuvers\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":10,\"maneuver\":{\"maneuverStraightAllowed\":false,\"maneuverNoStoppingAllowed\":false,\"goWithHalt\":false,\"maneuverLeftAllowed\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false}},\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":14,\"geometry\":\"LINESTRING (-105.0908389 39.5882151, -105.0908478 39.5882471)\",\"properties\":{\"laneId\":14,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":6,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":15,\"geometry\":\"LINESTRING (-105.0907875 39.58822, -105.0907979 39.5882514)\",\"properties\":{\"laneId\":15,\"laneType\":{\"vehicle\":{\"isVehicleRevocableLane\":false,\"isVehicleFlyOverLane\":false,\"permissionOnRequest\":false,\"hasIRbeaconCoverage\":false,\"restrictedToBusUse\":false,\"restrictedToTaxiUse\":false,\"restrictedFromPublicUse\":false,\"hovLaneUseOnly\":false}},\"sharedWith\":{\"busVehicleTraffic\":false,\"trackedVehicleTraffic\":false,\"individualMotorizedVehicleTraffic\":false,\"taxiVehicleTraffic\":false,\"overlappingLaneDescriptionProvided\":false,\"cyclistVehicleTraffic\":false,\"otherNonMotorizedTrafficTypes\":false,\"multipleLanesTreatedAsOneLane\":false,\"pedestrianTraffic\":false,\"pedestriansTraffic\":false},\"egressApproach\":6,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}}]},\"connectingLanesFeatureCollection\":{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"id\":\"1-15\",\"geometry\":\"LINESTRING (-105.0907089 39.587905, -105.0907875 39.58822)\",\"properties\":{\"signalGroupId\":2,\"ingressLaneId\":1,\"egressLaneId\":15}},{\"type\":\"Feature\",\"id\":\"2-14\",\"geometry\":\"LINESTRING (-105.0907462 39.5878956, -105.0908389 39.5882151)\",\"properties\":{\"signalGroupId\":2,\"ingressLaneId\":2,\"egressLaneId\":14}},{\"type\":\"Feature\",\"id\":\"3-10\",\"geometry\":\"LINESTRING (-105.0907914 39.5878879, -105.0911626 39.5880622)\",\"properties\":{\"signalGroupId\":2,\"ingressLaneId\":3,\"egressLaneId\":10}},{\"type\":\"Feature\",\"id\":\"8-15\",\"geometry\":\"LINESTRING (-105.0911477 39.587995, -105.0907875 39.58822)\",\"properties\":{\"signalGroupId\":4,\"ingressLaneId\":8,\"egressLaneId\":15}},{\"type\":\"Feature\",\"id\":\"7-6\",\"geometry\":\"LINESTRING (-105.0911442 39.5879589, -105.0910008 39.5878477)\",\"properties\":{\"ingressLaneId\":7,\"egressLaneId\":6}},{\"type\":\"Feature\",\"id\":\"9-14\",\"geometry\":\"LINESTRING (-105.0911534 39.5880261, -105.0908389 39.5882151)\",\"properties\":{\"signalGroupId\":4,\"ingressLaneId\":9,\"egressLaneId\":14}},{\"type\":\"Feature\",\"id\":\"12-5\",\"geometry\":\"LINESTRING (-105.0910447 39.5881948, -105.090959 39.5878557)\",\"properties\":{\"signalGroupId\":6,\"ingressLaneId\":12,\"egressLaneId\":5}},{\"type\":\"Feature\",\"id\":\"13-4\",\"geometry\":\"LINESTRING (-105.0910013 39.5881975, -105.090914 39.5878612)\",\"properties\":{\"signalGroupId\":6,\"ingressLaneId\":13,\"egressLaneId\":4}},{\"type\":\"Feature\",\"id\":\"11-10\",\"geometry\":\"LINESTRING (-105.0910891 39.5881859, -105.0911626 39.5880622)\",\"properties\":{\"ingressLaneId\":11,\"egressLaneId\":10}}]},\"properties\":{\"messageType\":\"MAP\",\"odeReceivedAt\":\"2024-05-03T20:58:48.748Z\",\"originIp\":\"10.11.81.12\",\"intersectionId\":12109,\"msgIssueRevision\":2,\"revision\":2,\"refPoint\":{\"latitude\":39.5880413,\"longitude\":-105.0908854,\"elevation\":1691},\"cti4501Conformant\":false,\"laneWidth\":366,\"mapSource\":\"RSU\",\"timeStamp\":\"2024-05-03T22:00:48.748Z\"}}";
+
+ String key = "{\"rsuId\":\"10.11.81.12\",\"intersectionId\":12109,\"region\":-1}";
+
+ @Autowired
+ DeduplicatorProperties props;
+
+
+ @Test
+ public void testTopology() {
+
+ props = new DeduplicatorProperties();
+ props.setKafkaTopicProcessedMapWKT(inputTopic);
+ props.setKafkaTopicDeduplicatedProcessedMapWKT(outputTopic);
+
+ ProcessedMapWktDeduplicatorTopology processedMapDeduplicatorTopology = new ProcessedMapWktDeduplicatorTopology(props, null);
+
+ Topology topology = processedMapDeduplicatorTopology.buildTopology();
+ objectMapper.registerModule(new JavaTimeModule());
+
+ try (TopologyTestDriver driver = new TopologyTestDriver(topology)) {
+
+
+ TestInputTopic inputProcessedMapData = driver.createInputTopic(
+ inputTopic,
+ Serdes.String().serializer(),
+ Serdes.String().serializer());
+
+
+ TestOutputTopic> outputProcessedMapData = driver.createOutputTopic(
+ outputTopic,
+ Serdes.String().deserializer(),
+ JsonSerdes.ProcessedMapWKT().deserializer());
+
+ inputProcessedMapData.pipeInput(key, inputProcessedMapWkt1);
+ inputProcessedMapData.pipeInput(key, inputProcessedMapWkt2);
+ inputProcessedMapData.pipeInput(key, inputProcessedMapWkt3);
+ inputProcessedMapData.pipeInput(key, inputProcessedMapWkt4);
+
+ List>> mapDeduplicatorResults = outputProcessedMapData.readKeyValuesToList();
+
+ // validate that only 3 messages make it through
+ assertEquals(3, mapDeduplicatorResults.size());
+
+ ProcessedMap map1 = objectMapper.readValue(inputProcessedMapWkt1, typeReference);
+ ProcessedMap map2 = objectMapper.readValue(inputProcessedMapWkt2, typeReference);
+ ProcessedMap map4 = objectMapper.readValue(inputProcessedMapWkt4, typeReference);
+
+ assertEquals(map1.getProperties().getOdeReceivedAt(), mapDeduplicatorResults.get(0).value.getProperties().getOdeReceivedAt());
+ assertEquals(map2.getProperties().getOdeReceivedAt(), mapDeduplicatorResults.get(1).value.getProperties().getOdeReceivedAt());
+ assertEquals(map4.getProperties().getOdeReceivedAt(), mapDeduplicatorResults.get(2).value.getProperties().getOdeReceivedAt());
+
+
+
+ } catch (JsonMappingException e) {
+ e.printStackTrace();
+ } catch (JsonProcessingException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/jpo-deduplicator/jpo-deduplicator/src/test/java/deduplicator/ProcessedSpatDeduplicatorTopologyTest.java b/jpo-deduplicator/jpo-deduplicator/src/test/java/deduplicator/ProcessedSpatDeduplicatorTopologyTest.java
new file mode 100644
index 0000000..96fd914
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/test/java/deduplicator/ProcessedSpatDeduplicatorTopologyTest.java
@@ -0,0 +1,106 @@
+package deduplicator;
+
+import org.apache.kafka.common.serialization.Serdes;
+import org.apache.kafka.streams.KeyValue;
+import org.apache.kafka.streams.TestInputTopic;
+import org.apache.kafka.streams.TestOutputTopic;
+import org.apache.kafka.streams.Topology;
+import org.apache.kafka.streams.TopologyTestDriver;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+
+import us.dot.its.jpo.deduplicator.DeduplicatorProperties;
+import us.dot.its.jpo.deduplicator.deduplicator.topologies.ProcessedSpatDeduplicatorTopology;
+import us.dot.its.jpo.geojsonconverter.pojos.spat.ProcessedSpat;
+import us.dot.its.jpo.geojsonconverter.serialization.JsonSerdes;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.List;
+
+
+public class ProcessedSpatDeduplicatorTopologyTest {
+
+ String inputTopic = "topic.ProcessedSpat";
+ String outputTopic = "topic.DeduplicatedProcessedSpat";
+
+
+ TypeReference typeReference = new TypeReference<>(){};
+ ObjectMapper objectMapper = new ObjectMapper();
+
+ String inputProcessedSpat1 = "{\"schemaVersion\":-1,\"messageType\":\"SPAT\",\"odeReceivedAt\":\"2024-11-01T15:49:05.278Z\",\"originIp\":\"10.164.6.16\",\"name\":\"N State St & E Center St\",\"intersectionId\":6311,\"cti4501Conformant\":false,\"validationMessages\":[{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].id\",\"schemaPath\":\"#/$defs/J2735IntersectionReferenceID/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[3].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[3].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[3].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[3].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[4].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[4].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[4].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[4].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[5].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[5].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[5].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[5].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing.maxEndTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[7].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[7].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[7].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[7].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"}],\"revision\":102,\"status\":{\"manualControlIsEnabled\":false,\"stopTimeIsActivated\":false,\"failureFlash\":false,\"preemptIsActive\":false,\"signalPriorityIsActive\":false,\"fixedTimeOperation\":false,\"trafficDependentOperation\":false,\"standbyOperation\":false,\"failureMode\":false,\"off\":false,\"recentMAPmessageUpdate\":true,\"recentChangeInMAPassignedLanesIDsUsed\":true,\"noValidMAPisAvailableAtThisTime\":false,\"noValidSPATisAvailableAtThisTime\":false},\"utcTimeStamp\":\"2024-11-01T15:49:05.278Z\",\"enabledLanes\":[],\"states\":[{\"signalGroup\":1,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:50:04.237Z\",\"maxEndTime\":\"2024-11-01T15:50:04.237Z\"}}]},{\"signalGroup\":2,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:49:08.337Z\",\"maxEndTime\":\"2024-11-01T15:49:08.337Z\"}}]},{\"signalGroup\":3,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:50:24.237Z\",\"maxEndTime\":\"2024-11-01T15:50:24.237Z\"}}]},{\"signalGroup\":4,\"stateTimeSpeed\":[{\"eventState\":\"PERMISSIVE_CLEARANCE\",\"timing\":{\"minEndTime\":\"2024-11-01T15:49:06.137Z\",\"maxEndTime\":\"2024-11-01T15:49:06.137Z\"}}]},{\"signalGroup\":5,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:49:08.337Z\",\"maxEndTime\":\"2024-11-01T15:49:08.337Z\"}}]},{\"signalGroup\":6,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:49:44.237Z\",\"maxEndTime\":\"2024-11-01T15:49:44.237Z\"}}]},{\"signalGroup\":7,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:49:05.237Z\"}}]},{\"signalGroup\":8,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:50:24.237Z\",\"maxEndTime\":\"2024-11-01T15:50:24.237Z\"}}]}]}";
+ String inputProcessedSpat2 = "{\"schemaVersion\":-1,\"messageType\":\"SPAT\",\"odeReceivedAt\":\"2024-11-01T15:49:05.278Z\",\"originIp\":\"10.164.6.16\",\"name\":\"N State St & E Center St\",\"intersectionId\":6311,\"cti4501Conformant\":false,\"validationMessages\":[{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].id\",\"schemaPath\":\"#/$defs/J2735IntersectionReferenceID/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[3].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[3].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[3].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[3].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[4].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[4].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[4].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[4].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[5].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[5].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[5].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[5].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing.maxEndTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[7].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[7].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[7].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[7].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"}],\"revision\":102,\"status\":{\"manualControlIsEnabled\":false,\"stopTimeIsActivated\":false,\"failureFlash\":false,\"preemptIsActive\":false,\"signalPriorityIsActive\":false,\"fixedTimeOperation\":false,\"trafficDependentOperation\":false,\"standbyOperation\":false,\"failureMode\":false,\"off\":false,\"recentMAPmessageUpdate\":true,\"recentChangeInMAPassignedLanesIDsUsed\":true,\"noValidMAPisAvailableAtThisTime\":false,\"noValidSPATisAvailableAtThisTime\":false},\"utcTimeStamp\":\"2024-11-01T15:49:05.378Z\",\"enabledLanes\":[],\"states\":[{\"signalGroup\":1,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:50:04.237Z\",\"maxEndTime\":\"2024-11-01T15:50:04.237Z\"}}]},{\"signalGroup\":2,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:49:08.337Z\",\"maxEndTime\":\"2024-11-01T15:49:08.337Z\"}}]},{\"signalGroup\":3,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:50:24.237Z\",\"maxEndTime\":\"2024-11-01T15:50:24.237Z\"}}]},{\"signalGroup\":4,\"stateTimeSpeed\":[{\"eventState\":\"PERMISSIVE_CLEARANCE\",\"timing\":{\"minEndTime\":\"2024-11-01T15:49:06.137Z\",\"maxEndTime\":\"2024-11-01T15:49:06.137Z\"}}]},{\"signalGroup\":5,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:49:08.337Z\",\"maxEndTime\":\"2024-11-01T15:49:08.337Z\"}}]},{\"signalGroup\":6,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:49:44.237Z\",\"maxEndTime\":\"2024-11-01T15:49:44.237Z\"}}]},{\"signalGroup\":7,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:49:05.237Z\"}}]},{\"signalGroup\":8,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:50:24.237Z\",\"maxEndTime\":\"2024-11-01T15:50:24.237Z\"}}]}]}";
+ String inputProcessedSpat3 = "{\"schemaVersion\":-1,\"messageType\":\"SPAT\",\"odeReceivedAt\":\"2024-11-01T15:49:05.278Z\",\"originIp\":\"10.164.6.16\",\"name\":\"N State St & E Center St\",\"intersectionId\":6311,\"cti4501Conformant\":false,\"validationMessages\":[{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].id\",\"schemaPath\":\"#/$defs/J2735IntersectionReferenceID/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[3].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[3].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[3].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[3].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[4].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[4].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[4].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[4].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[5].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[5].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[5].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[5].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing.maxEndTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[7].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[7].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[7].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[7].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"}],\"revision\":102,\"status\":{\"manualControlIsEnabled\":false,\"stopTimeIsActivated\":false,\"failureFlash\":false,\"preemptIsActive\":false,\"signalPriorityIsActive\":false,\"fixedTimeOperation\":false,\"trafficDependentOperation\":false,\"standbyOperation\":false,\"failureMode\":false,\"off\":false,\"recentMAPmessageUpdate\":true,\"recentChangeInMAPassignedLanesIDsUsed\":true,\"noValidMAPisAvailableAtThisTime\":false,\"noValidSPATisAvailableAtThisTime\":false},\"utcTimeStamp\":\"2024-11-01T15:50:05.478Z\",\"enabledLanes\":[],\"states\":[{\"signalGroup\":1,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:50:04.237Z\",\"maxEndTime\":\"2024-11-01T15:50:04.237Z\"}}]},{\"signalGroup\":2,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:49:08.337Z\",\"maxEndTime\":\"2024-11-01T15:49:08.337Z\"}}]},{\"signalGroup\":3,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:50:24.237Z\",\"maxEndTime\":\"2024-11-01T15:50:24.237Z\"}}]},{\"signalGroup\":4,\"stateTimeSpeed\":[{\"eventState\":\"PERMISSIVE_CLEARANCE\",\"timing\":{\"minEndTime\":\"2024-11-01T15:49:06.137Z\",\"maxEndTime\":\"2024-11-01T15:49:06.137Z\"}}]},{\"signalGroup\":5,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:49:08.337Z\",\"maxEndTime\":\"2024-11-01T15:49:08.337Z\"}}]},{\"signalGroup\":6,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:49:44.237Z\",\"maxEndTime\":\"2024-11-01T15:49:44.237Z\"}}]},{\"signalGroup\":7,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:49:05.237Z\"}}]},{\"signalGroup\":8,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:50:24.237Z\",\"maxEndTime\":\"2024-11-01T15:50:24.237Z\"}}]}]}";
+ String inputProcessedSpat4 = "{\"schemaVersion\":-1,\"messageType\":\"SPAT\",\"odeReceivedAt\":\"2024-11-01T15:49:05.278Z\",\"originIp\":\"10.164.6.16\",\"name\":\"N State St & E Center St\",\"intersectionId\":6311,\"cti4501Conformant\":false,\"validationMessages\":[{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].id\",\"schemaPath\":\"#/$defs/J2735IntersectionReferenceID/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[3].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[3].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[3].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[3].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[4].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[4].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[4].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[4].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[5].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[5].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[5].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[5].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing.maxEndTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[7].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[7].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[7].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[7].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"}],\"revision\":102,\"status\":{\"manualControlIsEnabled\":false,\"stopTimeIsActivated\":false,\"failureFlash\":false,\"preemptIsActive\":false,\"signalPriorityIsActive\":false,\"fixedTimeOperation\":false,\"trafficDependentOperation\":false,\"standbyOperation\":false,\"failureMode\":false,\"off\":false,\"recentMAPmessageUpdate\":true,\"recentChangeInMAPassignedLanesIDsUsed\":true,\"noValidMAPisAvailableAtThisTime\":false,\"noValidSPATisAvailableAtThisTime\":false},\"utcTimeStamp\":\"2024-11-01T15:50:05.578Z\",\"enabledLanes\":[],\"states\":[{\"signalGroup\":1,\"stateTimeSpeed\":[{\"eventState\":\"PROTECTED_MOVEMENT_ALLOWED\",\"timing\":{\"minEndTime\":\"2024-11-01T15:50:04.237Z\",\"maxEndTime\":\"2024-11-01T15:50:04.237Z\"}}]},{\"signalGroup\":2,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:49:08.337Z\",\"maxEndTime\":\"2024-11-01T15:49:08.337Z\"}}]},{\"signalGroup\":3,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:50:24.237Z\",\"maxEndTime\":\"2024-11-01T15:50:24.237Z\"}}]},{\"signalGroup\":4,\"stateTimeSpeed\":[{\"eventState\":\"PERMISSIVE_CLEARANCE\",\"timing\":{\"minEndTime\":\"2024-11-01T15:49:06.137Z\",\"maxEndTime\":\"2024-11-01T15:49:06.137Z\"}}]},{\"signalGroup\":5,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:49:08.337Z\",\"maxEndTime\":\"2024-11-01T15:49:08.337Z\"}}]},{\"signalGroup\":6,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:49:44.237Z\",\"maxEndTime\":\"2024-11-01T15:49:44.237Z\"}}]},{\"signalGroup\":7,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:49:05.237Z\"}}]},{\"signalGroup\":8,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:50:24.237Z\",\"maxEndTime\":\"2024-11-01T15:50:24.237Z\"}}]}]}";
+ String inputProcessedSpat5 = "{\"schemaVersion\":-1,\"messageType\":\"SPAT\",\"odeReceivedAt\":\"2024-11-01T15:49:05.278Z\",\"originIp\":\"10.164.6.16\",\"name\":\"N State St & E Center St\",\"intersectionId\":6311,\"cti4501Conformant\":false,\"validationMessages\":[{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].id\",\"schemaPath\":\"#/$defs/J2735IntersectionReferenceID/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[3].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[3].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[3].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[3].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[4].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[4].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[4].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[4].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[5].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[5].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[5].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[5].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing.maxEndTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[7].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[7].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[7].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[7].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"}],\"revision\":102,\"status\":{\"manualControlIsEnabled\":false,\"stopTimeIsActivated\":false,\"failureFlash\":false,\"preemptIsActive\":false,\"signalPriorityIsActive\":false,\"fixedTimeOperation\":false,\"trafficDependentOperation\":false,\"standbyOperation\":false,\"failureMode\":false,\"off\":false,\"recentMAPmessageUpdate\":true,\"recentChangeInMAPassignedLanesIDsUsed\":true,\"noValidMAPisAvailableAtThisTime\":false,\"noValidSPATisAvailableAtThisTime\":false},\"utcTimeStamp\":\"2024-11-01T15:50:05.678Z\",\"enabledLanes\":[],\"states\":[{\"signalGroup\":1,\"stateTimeSpeed\":[{\"eventState\":\"PROTECTED_MOVEMENT_ALLOWED\",\"timing\":{\"minEndTime\":\"2024-11-01T15:50:04.237Z\",\"maxEndTime\":\"2024-11-01T15:50:04.237Z\"}}]},{\"signalGroup\":2,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:49:08.337Z\",\"maxEndTime\":\"2024-11-01T15:49:08.337Z\"}}]},{\"signalGroup\":3,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:50:24.237Z\",\"maxEndTime\":\"2024-11-01T15:50:24.237Z\"}}]},{\"signalGroup\":4,\"stateTimeSpeed\":[{\"eventState\":\"PERMISSIVE_CLEARANCE\",\"timing\":{\"minEndTime\":\"2024-11-01T15:49:06.137Z\",\"maxEndTime\":\"2024-11-01T15:49:06.137Z\"}}]},{\"signalGroup\":5,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:49:08.337Z\",\"maxEndTime\":\"2024-11-01T15:49:08.337Z\"}}]},{\"signalGroup\":6,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:49:44.237Z\",\"maxEndTime\":\"2024-11-01T15:49:44.237Z\"}}]},{\"signalGroup\":7,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:49:05.237Z\"}}]}]}";
+ String inputProcessedSpat6 = "{\"schemaVersion\":-1,\"messageType\":\"SPAT\",\"odeReceivedAt\":\"2024-11-01T15:49:05.278Z\",\"originIp\":\"10.164.6.16\",\"name\":\"N State St & E Center St\",\"intersectionId\":6311,\"cti4501Conformant\":false,\"validationMessages\":[{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].id\",\"schemaPath\":\"#/$defs/J2735IntersectionReferenceID/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[3].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[3].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[3].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[3].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[4].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[4].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[4].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[4].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[5].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[5].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[5].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[5].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing.maxEndTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[6].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[7].state_time_speed.movementEventList[0].timing.startTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[7].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[7].state_time_speed.movementEventList[0].timing.nextTime: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[7].state_time_speed.movementEventList[0].timing\",\"schemaPath\":\"#/$defs/J2735TimeChangeDetails/required\"}],\"revision\":102,\"status\":{\"manualControlIsEnabled\":false,\"stopTimeIsActivated\":false,\"failureFlash\":false,\"preemptIsActive\":false,\"signalPriorityIsActive\":false,\"fixedTimeOperation\":false,\"trafficDependentOperation\":false,\"standbyOperation\":false,\"failureMode\":false,\"off\":false,\"recentMAPmessageUpdate\":true,\"recentChangeInMAPassignedLanesIDsUsed\":true,\"noValidMAPisAvailableAtThisTime\":false,\"noValidSPATisAvailableAtThisTime\":false},\"utcTimeStamp\":\"2024-11-01T15:50:05.778Z\",\"enabledLanes\":[],\"states\":[{\"signalGroup\":1,\"stateTimeSpeed\":[{\"eventState\":\"PROTECTED_MOVEMENT_ALLOWED\",\"timing\":{\"minEndTime\":\"2024-11-01T15:50:04.237Z\",\"maxEndTime\":\"2024-11-01T15:50:04.237Z\"}}]},{\"signalGroup\":2,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:49:08.337Z\",\"maxEndTime\":\"2024-11-01T15:49:08.337Z\"}}]},{\"signalGroup\":3,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:50:24.237Z\",\"maxEndTime\":\"2024-11-01T15:50:24.237Z\"}}]},{\"signalGroup\":4,\"stateTimeSpeed\":[{\"eventState\":\"PERMISSIVE_CLEARANCE\",\"timing\":{\"minEndTime\":\"2024-11-01T15:49:06.137Z\",\"maxEndTime\":\"2024-11-01T15:49:06.137Z\"}}]},{\"signalGroup\":5,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:49:08.337Z\",\"maxEndTime\":\"2024-11-01T15:49:08.337Z\"}}]},{\"signalGroup\":6,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:49:44.237Z\",\"maxEndTime\":\"2024-11-01T15:49:44.237Z\"}}]},{\"signalGroup\":9,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-11-01T15:49:05.237Z\"}}]}]}";
+
+ String key = "{\"rsuId\": \"10.164.6.16\", \"intersectionId\": 6311, \"region\": -1}";
+
+ @Autowired
+ DeduplicatorProperties props;
+
+
+ @Test
+ public void testTopology() {
+
+ props = new DeduplicatorProperties();
+ props.setKafkaTopicProcessedSpat(inputTopic);
+ props.setKafkaTopicDeduplicatedProcessedSpat(outputTopic);
+
+ ProcessedSpatDeduplicatorTopology processedSpatDeduplicatorTopology = new ProcessedSpatDeduplicatorTopology(props, null);
+
+ Topology topology = processedSpatDeduplicatorTopology.buildTopology();
+ objectMapper.registerModule(new JavaTimeModule());
+
+ try (TopologyTestDriver driver = new TopologyTestDriver(topology)) {
+
+
+ TestInputTopic inputProcessedSpatData = driver.createInputTopic(
+ inputTopic,
+ Serdes.String().serializer(),
+ Serdes.String().serializer());
+
+
+ TestOutputTopic outputProcessedSpatData = driver.createOutputTopic(
+ outputTopic,
+ Serdes.String().deserializer(),
+ JsonSerdes.ProcessedSpat().deserializer());
+
+ inputProcessedSpatData.pipeInput(key, inputProcessedSpat1);
+ inputProcessedSpatData.pipeInput(key, inputProcessedSpat2);
+ inputProcessedSpatData.pipeInput(key, inputProcessedSpat3);
+ inputProcessedSpatData.pipeInput(key, inputProcessedSpat4);
+ inputProcessedSpatData.pipeInput(key, inputProcessedSpat5);
+ inputProcessedSpatData.pipeInput(key, inputProcessedSpat6);
+
+ List> spatDeduplicatorResults = outputProcessedSpatData.readKeyValuesToList();
+
+ // validate that only 5 messages make it through
+ assertEquals(5, spatDeduplicatorResults.size());
+
+ ProcessedSpat spat1 = objectMapper.readValue(inputProcessedSpat1, typeReference);
+ ProcessedSpat spat3 = objectMapper.readValue(inputProcessedSpat3, typeReference); // forwarded because the time on the message changes by 1 minute
+ ProcessedSpat spat4 = objectMapper.readValue(inputProcessedSpat4, typeReference); // forwarded because the signal state changes
+ ProcessedSpat spat5 = objectMapper.readValue(inputProcessedSpat5, typeReference); // forwarded because the number of signal groups changed
+ ProcessedSpat spat6 = objectMapper.readValue(inputProcessedSpat6, typeReference); // forwarded because a signal group ID changed
+
+ assertEquals(spat1.getUtcTimeStamp(), spatDeduplicatorResults.get(0).value.getUtcTimeStamp());
+ assertEquals(spat3.getUtcTimeStamp(), spatDeduplicatorResults.get(1).value.getUtcTimeStamp());
+ assertEquals(spat4.getUtcTimeStamp(), spatDeduplicatorResults.get(2).value.getUtcTimeStamp());
+ assertEquals(spat5.getUtcTimeStamp(), spatDeduplicatorResults.get(3).value.getUtcTimeStamp());
+ assertEquals(spat6.getUtcTimeStamp(), spatDeduplicatorResults.get(4).value.getUtcTimeStamp());
+
+
+ } catch (JsonMappingException e) {
+ e.printStackTrace();
+ } catch (JsonProcessingException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/jpo-deduplicator/jpo-deduplicator/src/test/java/deduplicator/TimDeduplicatorTopologyTest.java b/jpo-deduplicator/jpo-deduplicator/src/test/java/deduplicator/TimDeduplicatorTopologyTest.java
new file mode 100644
index 0000000..164dd22
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/test/java/deduplicator/TimDeduplicatorTopologyTest.java
@@ -0,0 +1,89 @@
+package deduplicator;
+
+import org.apache.kafka.common.serialization.Serdes;
+import org.apache.kafka.streams.KeyValue;
+import org.apache.kafka.streams.TestInputTopic;
+import org.apache.kafka.streams.TestOutputTopic;
+import org.apache.kafka.streams.Topology;
+import org.apache.kafka.streams.TopologyTestDriver;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+
+import us.dot.its.jpo.deduplicator.DeduplicatorProperties;
+import us.dot.its.jpo.deduplicator.deduplicator.topologies.TimDeduplicatorTopology;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.List;
+
+
+public class TimDeduplicatorTopologyTest {
+
+ String inputTopic = "topic.OdeTimJson";
+ String outputTopic = "topic.DeduplicatedOdeTimJson";
+
+ ObjectMapper objectMapper = new ObjectMapper();
+
+ //Original Message
+ String inputTim1 = "{\"metadata\":{\"securityResultCode\":\"success\",\"recordGeneratedBy\":\"RSU\",\"schemaVersion\":\"6\",\"odePacketID\":\"\",\"sanitized\":\"false\",\"recordType\":\"timMsg\",\"recordGeneratedAt\":\"\",\"maxDurationTime\":\"0\",\"odeTimStartDateTime\":\"\",\"receivedMessageDetails\":\"\",\"payloadType\":\"us.dot.its.jpo.ode.model.OdeTimPayload\",\"serialId\":{\"recordId\":\"0\",\"serialNumber\":\"0\",\"streamId\":\"d052115a-5289-4da3-bc9f-12edca1d2c46\",\"bundleSize\":\"1\",\"bundleId\":\"0\"},\"logFileName\":\"\",\"odeReceivedAt\":\"2024-07-23T18:33:17.328Z\",\"originIp\":\"10.16.28.54\"},\"payload\":{\"data\":{\"MessageFrame\":{\"messageId\":\"31\",\"value\":{\"TravelerInformation\":{\"timeStamp\":\"264703\",\"packetID\":\"0350C30EB1A83736D8\",\"urlB\":\"null\",\"dataFrames\":{\"TravelerDataFrame\":{\"durationTime\":\"30\",\"regions\":{\"GeographicalPath\":{\"closedPath\":{\"false\":\"\"},\"anchor\":{\"lat\":\"395658598\",\"long\":\"-1050401840\"},\"name\":\"I_CO-470_RSU_172.16.28.116\",\"laneWidth\":\"5000\",\"directionality\":{\"both\":\"\"},\"description\":{\"path\":{\"offset\":{\"ll\":{\"nodes\":{\"NodeLL\":[{\"delta\":{\"node-LL1\":{\"lon\":\"1527\",\"lat\":\"-659\"}}},{\"delta\":{\"node-LL2\":{\"lon\":\"5545\",\"lat\":\"-2394\"}}},{\"delta\":{\"node-LL3\":{\"lon\":\"9493\",\"lat\":\"-2736\"}}},{\"delta\":{\"node-LL2\":{\"lon\":\"7465\",\"lat\":\"-1304\"}}},{\"delta\":{\"node-LL4\":{\"lon\":\"34464\",\"lat\":\"-4324\"}}},{\"delta\":{\"node-LL3\":{\"lon\":\"9994\",\"lat\":\"-1119\"}}},{\"delta\":{\"node-LL3\":{\"lon\":\"20051\",\"lat\":\"-2246\"}}},{\"delta\":{\"node-LL3\":{\"lon\":\"31738\",\"lat\":\"-1775\"}}},{\"delta\":{\"node-LL2\":{\"lon\":\"5744\",\"lat\":\"-315\"}}}]}}},\"scale\":\"0\"}},\"id\":{\"id\":\"0\",\"region\":\"0\"},\"direction\":\"0000110000000000\"}},\"startYear\":\"2024\",\"notUsed2\":\"0\",\"msgId\":{\"roadSignID\":{\"viewAngle\":\"1111111111111111\",\"mutcdCode\":{\"warning\":\"\"},\"position\":{\"lat\":\"395658598\",\"long\":\"-1050401840\"}}},\"notUsed3\":\"0\",\"notUsed1\":\"0\",\"priority\":\"5\",\"content\":{\"workZone\":{\"SEQUENCE\":{\"item\":{\"itis\":\"1025\"}}}},\"url\":\"null\",\"notUsed\":\"0\",\"frameType\":{\"advisory\":\"\"},\"startTime\":\"277110\"}},\"msgCnt\":\"1\"}}}},\"dataType\":\"TravelerInformation\"}}";
+
+ // Shifted Forward .1 seconds - Should be deduplicated
+ String inputTim2 = "{\"metadata\":{\"securityResultCode\":\"success\",\"recordGeneratedBy\":\"RSU\",\"schemaVersion\":\"6\",\"odePacketID\":\"\",\"sanitized\":\"false\",\"recordType\":\"timMsg\",\"recordGeneratedAt\":\"\",\"maxDurationTime\":\"0\",\"odeTimStartDateTime\":\"\",\"receivedMessageDetails\":\"\",\"payloadType\":\"us.dot.its.jpo.ode.model.OdeTimPayload\",\"serialId\":{\"recordId\":\"0\",\"serialNumber\":\"0\",\"streamId\":\"d052115a-5289-4da3-bc9f-12edca1d2c46\",\"bundleSize\":\"1\",\"bundleId\":\"0\"},\"logFileName\":\"\",\"odeReceivedAt\":\"2024-07-23T18:33:17.428Z\",\"originIp\":\"10.16.28.54\"},\"payload\":{\"data\":{\"MessageFrame\":{\"messageId\":\"31\",\"value\":{\"TravelerInformation\":{\"timeStamp\":\"264703\",\"packetID\":\"0350C30EB1A83736D8\",\"urlB\":\"null\",\"dataFrames\":{\"TravelerDataFrame\":{\"durationTime\":\"30\",\"regions\":{\"GeographicalPath\":{\"closedPath\":{\"false\":\"\"},\"anchor\":{\"lat\":\"395658598\",\"long\":\"-1050401840\"},\"name\":\"I_CO-470_RSU_172.16.28.116\",\"laneWidth\":\"5000\",\"directionality\":{\"both\":\"\"},\"description\":{\"path\":{\"offset\":{\"ll\":{\"nodes\":{\"NodeLL\":[{\"delta\":{\"node-LL1\":{\"lon\":\"1527\",\"lat\":\"-659\"}}},{\"delta\":{\"node-LL2\":{\"lon\":\"5545\",\"lat\":\"-2394\"}}},{\"delta\":{\"node-LL3\":{\"lon\":\"9493\",\"lat\":\"-2736\"}}},{\"delta\":{\"node-LL2\":{\"lon\":\"7465\",\"lat\":\"-1304\"}}},{\"delta\":{\"node-LL4\":{\"lon\":\"34464\",\"lat\":\"-4324\"}}},{\"delta\":{\"node-LL3\":{\"lon\":\"9994\",\"lat\":\"-1119\"}}},{\"delta\":{\"node-LL3\":{\"lon\":\"20051\",\"lat\":\"-2246\"}}},{\"delta\":{\"node-LL3\":{\"lon\":\"31738\",\"lat\":\"-1775\"}}},{\"delta\":{\"node-LL2\":{\"lon\":\"5744\",\"lat\":\"-315\"}}}]}}},\"scale\":\"0\"}},\"id\":{\"id\":\"0\",\"region\":\"0\"},\"direction\":\"0000110000000000\"}},\"startYear\":\"2024\",\"notUsed2\":\"0\",\"msgId\":{\"roadSignID\":{\"viewAngle\":\"1111111111111111\",\"mutcdCode\":{\"warning\":\"\"},\"position\":{\"lat\":\"395658598\",\"long\":\"-1050401840\"}}},\"notUsed3\":\"0\",\"notUsed1\":\"0\",\"priority\":\"5\",\"content\":{\"workZone\":{\"SEQUENCE\":{\"item\":{\"itis\":\"1025\"}}}},\"url\":\"null\",\"notUsed\":\"0\",\"frameType\":{\"advisory\":\"\"},\"startTime\":\"277110\"}},\"msgCnt\":\"1\"}}}},\"dataType\":\"TravelerInformation\"}}";
+
+ // Shifted Forward 1 hour Should be allowed to pass through
+ String inputTim3 = "{\"metadata\":{\"securityResultCode\":\"success\",\"recordGeneratedBy\":\"RSU\",\"schemaVersion\":\"6\",\"odePacketID\":\"\",\"sanitized\":\"false\",\"recordType\":\"timMsg\",\"recordGeneratedAt\":\"\",\"maxDurationTime\":\"0\",\"odeTimStartDateTime\":\"\",\"receivedMessageDetails\":\"\",\"payloadType\":\"us.dot.its.jpo.ode.model.OdeTimPayload\",\"serialId\":{\"recordId\":\"0\",\"serialNumber\":\"0\",\"streamId\":\"d052115a-5289-4da3-bc9f-12edca1d2c46\",\"bundleSize\":\"1\",\"bundleId\":\"0\"},\"logFileName\":\"\",\"odeReceivedAt\":\"2024-07-23T19:33:17.428Z\",\"originIp\":\"10.16.28.54\"},\"payload\":{\"data\":{\"MessageFrame\":{\"messageId\":\"31\",\"value\":{\"TravelerInformation\":{\"timeStamp\":\"264703\",\"packetID\":\"0350C30EB1A83736D8\",\"urlB\":\"null\",\"dataFrames\":{\"TravelerDataFrame\":{\"durationTime\":\"30\",\"regions\":{\"GeographicalPath\":{\"closedPath\":{\"false\":\"\"},\"anchor\":{\"lat\":\"395658598\",\"long\":\"-1050401840\"},\"name\":\"I_CO-470_RSU_172.16.28.116\",\"laneWidth\":\"5000\",\"directionality\":{\"both\":\"\"},\"description\":{\"path\":{\"offset\":{\"ll\":{\"nodes\":{\"NodeLL\":[{\"delta\":{\"node-LL1\":{\"lon\":\"1527\",\"lat\":\"-659\"}}},{\"delta\":{\"node-LL2\":{\"lon\":\"5545\",\"lat\":\"-2394\"}}},{\"delta\":{\"node-LL3\":{\"lon\":\"9493\",\"lat\":\"-2736\"}}},{\"delta\":{\"node-LL2\":{\"lon\":\"7465\",\"lat\":\"-1304\"}}},{\"delta\":{\"node-LL4\":{\"lon\":\"34464\",\"lat\":\"-4324\"}}},{\"delta\":{\"node-LL3\":{\"lon\":\"9994\",\"lat\":\"-1119\"}}},{\"delta\":{\"node-LL3\":{\"lon\":\"20051\",\"lat\":\"-2246\"}}},{\"delta\":{\"node-LL3\":{\"lon\":\"31738\",\"lat\":\"-1775\"}}},{\"delta\":{\"node-LL2\":{\"lon\":\"5744\",\"lat\":\"-315\"}}}]}}},\"scale\":\"0\"}},\"id\":{\"id\":\"0\",\"region\":\"0\"},\"direction\":\"0000110000000000\"}},\"startYear\":\"2024\",\"notUsed2\":\"0\",\"msgId\":{\"roadSignID\":{\"viewAngle\":\"1111111111111111\",\"mutcdCode\":{\"warning\":\"\"},\"position\":{\"lat\":\"395658598\",\"long\":\"-1050401840\"}}},\"notUsed3\":\"0\",\"notUsed1\":\"0\",\"priority\":\"5\",\"content\":{\"workZone\":{\"SEQUENCE\":{\"item\":{\"itis\":\"1025\"}}}},\"url\":\"null\",\"notUsed\":\"0\",\"frameType\":{\"advisory\":\"\"},\"startTime\":\"277110\"}},\"msgCnt\":\"1\"}}}},\"dataType\":\"TravelerInformation\"}}";
+
+ // Has a different payload ID. Should be allowed through
+ String inputTim4 = "{\"metadata\":{\"securityResultCode\":\"success\",\"recordGeneratedBy\":\"RSU\",\"schemaVersion\":\"6\",\"odePacketID\":\"\",\"sanitized\":\"false\",\"recordType\":\"timMsg\",\"recordGeneratedAt\":\"\",\"maxDurationTime\":\"0\",\"odeTimStartDateTime\":\"\",\"receivedMessageDetails\":\"\",\"payloadType\":\"us.dot.its.jpo.ode.model.OdeTimPayload\",\"serialId\":{\"recordId\":\"0\",\"serialNumber\":\"0\",\"streamId\":\"d052115a-5289-4da3-bc9f-12edca1d2c46\",\"bundleSize\":\"1\",\"bundleId\":\"0\"},\"logFileName\":\"\",\"odeReceivedAt\":\"2024-07-23T19:34:17.328Z\",\"originIp\":\"10.16.28.54\"},\"payload\":{\"data\":{\"MessageFrame\":{\"messageId\":\"31\",\"value\":{\"TravelerInformation\":{\"timeStamp\":\"264703\",\"packetID\":\"0350C30EB1A83736D9\",\"urlB\":\"null\",\"dataFrames\":{\"TravelerDataFrame\":{\"durationTime\":\"30\",\"regions\":{\"GeographicalPath\":{\"closedPath\":{\"false\":\"\"},\"anchor\":{\"lat\":\"395658598\",\"long\":\"-1050401840\"},\"name\":\"I_CO-470_RSU_172.16.28.116\",\"laneWidth\":\"10000\",\"directionality\":{\"both\":\"\"},\"description\":{\"path\":{\"offset\":{\"ll\":{\"nodes\":{\"NodeLL\":[{\"delta\":{\"node-LL1\":{\"lon\":\"1527\",\"lat\":\"-659\"}}},{\"delta\":{\"node-LL2\":{\"lon\":\"5545\",\"lat\":\"-2394\"}}},{\"delta\":{\"node-LL3\":{\"lon\":\"9493\",\"lat\":\"-2736\"}}},{\"delta\":{\"node-LL2\":{\"lon\":\"7465\",\"lat\":\"-1304\"}}},{\"delta\":{\"node-LL4\":{\"lon\":\"34464\",\"lat\":\"-4324\"}}},{\"delta\":{\"node-LL3\":{\"lon\":\"9994\",\"lat\":\"-1119\"}}},{\"delta\":{\"node-LL3\":{\"lon\":\"20051\",\"lat\":\"-2246\"}}},{\"delta\":{\"node-LL3\":{\"lon\":\"31738\",\"lat\":\"-1775\"}}},{\"delta\":{\"node-LL2\":{\"lon\":\"5744\",\"lat\":\"-315\"}}}]}}},\"scale\":\"0\"}},\"id\":{\"id\":\"0\",\"region\":\"0\"},\"direction\":\"0000110000000000\"}},\"startYear\":\"2024\",\"notUsed2\":\"0\",\"msgId\":{\"roadSignID\":{\"viewAngle\":\"1111111111111111\",\"mutcdCode\":{\"warning\":\"\"},\"position\":{\"lat\":\"395658598\",\"long\":\"-1050401840\"}}},\"notUsed3\":\"0\",\"notUsed1\":\"0\",\"priority\":\"5\",\"content\":{\"workZone\":{\"SEQUENCE\":{\"item\":{\"itis\":\"1025\"}}}},\"url\":\"null\",\"notUsed\":\"0\",\"frameType\":{\"advisory\":\"\"},\"startTime\":\"277110\"}},\"msgCnt\":\"1\"}}}},\"dataType\":\"TravelerInformation\"}}";
+
+ @Autowired
+ DeduplicatorProperties props;
+
+ @Test
+ public void testTopology() {
+
+ props = new DeduplicatorProperties();
+ props.setKafkaTopicOdeTimJson(inputTopic);
+ props.setKafkaTopicDeduplicatedOdeTimJson(outputTopic);
+
+ TimDeduplicatorTopology TimDeduplicatorTopology = new TimDeduplicatorTopology(props, null);
+
+ Topology topology = TimDeduplicatorTopology.buildTopology();
+ objectMapper.registerModule(new JavaTimeModule());
+
+ try (TopologyTestDriver driver = new TopologyTestDriver(topology)) {
+
+
+ TestInputTopic inputTimData = driver.createInputTopic(
+ inputTopic,
+ Serdes.Void().serializer(),
+ Serdes.String().serializer());
+
+
+ TestOutputTopic outputTimData = driver.createOutputTopic(
+ outputTopic,
+ Serdes.String().deserializer(),
+ Serdes.String().deserializer());
+
+ inputTimData.pipeInput(null, inputTim1);
+ inputTimData.pipeInput(null, inputTim2);
+ inputTimData.pipeInput(null, inputTim3);
+ inputTimData.pipeInput(null, inputTim4);
+
+ List> timDeduplicatedResults = outputTimData.readKeyValuesToList();
+
+ // validate that only 3 messages make it through
+ assertEquals(3, timDeduplicatedResults.size());
+ inputTim1 = inputTim1.strip();
+
+ assertEquals(inputTim1.replace(" ", ""), timDeduplicatedResults.get(0).value.replace(" ", ""));
+ assertEquals(inputTim3.replace(" ", ""), timDeduplicatedResults.get(1).value.replace(" ", ""));
+ assertEquals(inputTim4.replace(" ", ""), timDeduplicatedResults.get(2).value.replace(" ", ""));
+
+ }catch(Exception e){
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/jpo-deduplicator/jpo-deduplicator/src/test/resources/application-testConfig.yaml b/jpo-deduplicator/jpo-deduplicator/src/test/resources/application-testConfig.yaml
new file mode 100644
index 0000000..4f102d0
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/test/resources/application-testConfig.yaml
@@ -0,0 +1 @@
+spring.kafka.bootstrap-servers: localhost:10093
\ No newline at end of file
diff --git a/jpo-deduplicator/jpo-deduplicator/src/test/resources/logback-test.xml b/jpo-deduplicator/jpo-deduplicator/src/test/resources/logback-test.xml
new file mode 100644
index 0000000..cd07f49
--- /dev/null
+++ b/jpo-deduplicator/jpo-deduplicator/src/test/resources/logback-test.xml
@@ -0,0 +1,11 @@
+
+
+
+ %msg%n
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/kafka-connect/Dockerfile b/kafka-connect/Dockerfile
index 0be3886..0ba8d69 100644
--- a/kafka-connect/Dockerfile
+++ b/kafka-connect/Dockerfile
@@ -1,10 +1,6 @@
FROM confluentinc/cp-kafka-connect:7.7.0
-COPY connect_wait.sh /scripts/connect_wait.sh
-
# Docs: https://www.mongodb.com/docs/kafka-connector/current/
RUN confluent-hub install --no-prompt mongodb/kafka-connect-mongodb:1.13.0
# Docs: https://docs.confluent.io/platform/current/connect/transforms/overview.html
-RUN confluent-hub install --no-prompt confluentinc/connect-transforms:1.4.7
-
-CMD ["bash", "-c", "/scripts/connect_wait.sh"]
\ No newline at end of file
+RUN confluent-hub install --no-prompt confluentinc/connect-transforms:1.4.7
\ No newline at end of file
diff --git a/kafka-connect/connect_start.sh b/kafka-connect/connect_start.sh
deleted file mode 100644
index 2a487ec..0000000
--- a/kafka-connect/connect_start.sh
+++ /dev/null
@@ -1,105 +0,0 @@
-# bin/bash
-echo "------------------------------------------"
-echo "Kafka connector creation started."
-echo "------------------------------------------"
-
-
-declare -A OdeBsmJson=([name]="topic.OdeBsmJson" [collection]="OdeBsmJson"
- [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true)
-
-declare -A OdeMapJson=([name]="topic.OdeMapJson" [collection]="OdeMapJson"
- [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true)
-
-declare -A OdeSpatJson=([name]="topic.OdeSpatJson" [collection]="OdeSpatJson"
- [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true)
-
-declare -A OdeTimJson=([name]="topic.OdeTimJson" [collection]="OdeTimJson"
- [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true)
-
-declare -A OdePsmJson=([name]="topic.OdePsmJson" [collection]="OdePsmJson"
- [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true)
-
-function createSink() {
- local -n topic=$1
- local name=${topic[name]}
- local collection=${topic[collection]}
- local timefield=${topic[timefield]}
- local convert_timestamp=${topic[convert_timestamp]}
- local use_key=${topic[use_key]}
- local key=${topic[key]}
- local add_timestamp=${topic[add_timestamp]}
-
- echo "Creating sink connector with parameters:"
- echo "name=$name"
- echo "collection=$collection"
- echo "timefield=$timefield"
- echo "convert_timestamp=$convert_timestamp"
- echo "use_key=$use_key"
- echo "key=$key"
- echo "add_timestamp=$add_timestamp"
-
- local connectConfig=' {
- "group.id":"connector-consumer",
- "connector.class":"com.mongodb.kafka.connect.MongoSinkConnector",
- "tasks.max":3,
- "topics":"'$name'",
- "connection.uri":"'$MONGO_URI'",
- "database":"'$MONGO_DB_NAME'",
- "collection":"'$collection'",
- "key.converter":"org.apache.kafka.connect.storage.StringConverter",
- "key.converter.schemas.enable":false,
- "value.converter":"org.apache.kafka.connect.json.JsonConverter",
- "value.converter.schemas.enable":false,
- "errors.tolerance": "all",
- "mongo.errors.tolerance": "all",
- "errors.deadletterqueue.topic.name": "",
- "errors.log.enable": false,
- "errors.log.include.messages": false,
- "errors.deadletterqueue.topic.replication.factor": 0'
-
-
- if [ "$convert_timestamp" == true ]
- then
- local connectConfig=''$connectConfig',
- "transforms": "TimestampConverter",
- "transforms.TimestampConverter.field": "'$timefield'",
- "transforms.TimestampConverter.type": "org.apache.kafka.connect.transforms.TimestampConverter$Value",
- "transforms.TimestampConverter.target.type": "Timestamp"'
- fi
-
- if [ "$add_timestamp" == true ]
- then
- local connectConfig=''$connectConfig',
- "transforms": "AddTimestamp,AddedTimestampConverter",
- "transforms.AddTimestamp.type": "org.apache.kafka.connect.transforms.InsertField$Value",
- "transforms.AddTimestamp.timestamp.field": "recordGeneratedAt",
- "transforms.AddedTimestampConverter.field": "recordGeneratedAt",
- "transforms.AddedTimestampConverter.type": "org.apache.kafka.connect.transforms.TimestampConverter$Value",
- "transforms.AddedTimestampConverter.target.type": "Timestamp"'
- fi
-
- if [ "$use_key" == true ]
- then
- local connectConfig=''$connectConfig',
- "document.id.strategy": "com.mongodb.kafka.connect.sink.processor.id.strategy.PartialValueStrategy",
- "document.id.strategy.partial.value.projection.list": "'$key'",
- "document.id.strategy.partial.value.projection.type": "AllowList",
- "document.id.strategy.overwrite.existing": true'
- fi
-
- local connectConfig=''$connectConfig' }'
-
- echo " Creating connector with Config : $connectConfig"
-
- curl -X PUT http://localhost:8083/connectors/MongoSink.${name}/config -H "Content-Type: application/json" -d "$connectConfig"
-}
-
-createSink OdeBsmJson
-createSink OdeMapJson
-createSink OdeSpatJson
-createSink OdeTimJson
-createSink OdePsmJson
-
-echo "----------------------------------"
-echo "ODE Kafka connector creation complete!"
-echo "----------------------------------"
\ No newline at end of file
diff --git a/kafka-connect/connect_wait.sh b/kafka-connect/connect_wait.sh
deleted file mode 100644
index 421faef..0000000
--- a/kafka-connect/connect_wait.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-
-/etc/confluent/docker/run &
-echo "Waiting for Kafka Connect to start listening on kafka-connect"
-while [ $(curl -s -o /dev/null -w %{http_code} http://localhost:8083/connectors) -eq 000 ] ; do
- echo -e $(date) " Kafka Connect listener HTTP state: " $(curl -s -o /dev/null -w %{http_code} http://localhost:8083/connectors) " (waiting for 200)"
- sleep 5
-done
-sleep 10
-echo -e "\n--\n+> Creating Kafka Connect MongoDB sink"
-
-# Check if connect_start.sh exists
-if [ ! -f /scripts/connect_start.sh ]; then
- echo "Error: connect_start.sh does not exist, starting without any connectors."
-else
- echo "Connect_start.sh exists, starting with connectors."
- bash /scripts/connect_start.sh
-fi
-
-sleep infinity
\ No newline at end of file
diff --git a/mongo/create_indexes.js b/mongo/create_indexes.js
index 4457a5b..96fffe8 100644
--- a/mongo/create_indexes.js
+++ b/mongo/create_indexes.js
@@ -1,150 +1,256 @@
// Create indexes on all collections
/*
-This script is responsible for initializing the replica set, creating collections, adding indexes and TTLs
+This is the second script responsible for configuring mongoDB automatically on startup.
+This script is responsible for creating users, creating collections, adding indexes, and configuring TTLs
+For more information see the header in a_init_replicas.js
*/
+
+console.log("");
console.log("Running create_indexes.js");
-const ode_db = process.env.MONGO_DB_NAME;
-const rw_user = process.env.MONGO_READ_WRITE_USER;
-const rw_pass = process.env.MONGO_READ_WRITE_PASS;
+Object.keys(process.env).forEach(key => {
+ console.log(`${key}: ${process.env[key]}`);
+});
-const ttlInDays = process.env.MONGO_COLLECTION_TTL; // TTL in days
-const expire_seconds = ttlInDays * 24 * 60 * 60;
-const retry_milliseconds = 5000;
-console.log("DB Name: " + ode_db);
+// Setup Username and Password Definitions
+const MONGO_ROOT_USERNAME = process.env['MONGO_ADMIN_DB_USER'];
+const MONGO_ROOT_PASSWORD = process.env['MONGO_ADMIN_DB_PASS'];
-try {
- console.log("Initializing replica set...");
+const MONGO_READ_WRITE_USER=process.env['MONGO_READ_WRITE_USER'];
+const MONGO_READ_WRITE_PASS=process.env['MONGO_READ_WRITE_PASS'];
+
+const MONGO_READ_USER = process.env['MONGO_READ_USER'];
+const MONGO_READ_PASS = process.env['MONGO_READ_PASS'];
+
+// Prometheus Exporter User
+const MONGO_EXPORTER_USERNAME = process.env['MONGO_EXPORTER_USERNAME'];
+const MONGO_EXPORTER_PASSWORD = process.env['MONGO_EXPORTER_PASSWORD'];
+
+const MONGO_DB_NAME = process.env['MONGO_DB_NAME'] || "CV";
+
+const expireSeconds = Number(process.env['MONGO_DATA_RETENTION_SECONDS']) || 5184000; // 2 months
+const ttlExpireSeconds = Number(process.env['MONGO_ASN_RETENTION_SECONDS']) || 86400; // 24 hours
+const retryMilliseconds = 10000;
+
+const CONNECT_CREATE_ODE = process.env['CONNECT_CREATE_ODE'] || true;
+const CONNECT_CREATE_GEOJSONCONVERTER = process.env['CONNECT_CREATE_GEOJSONCONVERTER'] || true;
+const CONNECT_CREATE_CONFLICTMONITOR = process.env['CONNECT_CREATE_CONFLICTMONITOR'] || true;
+const CONNECT_CREATE_DEDUPLICATOR = process.env['CONNECT_CREATE_DEDUPLICATOR'] || true;
+
+
+const users = [
+ // {username: CM_MONGO_ROOT_USERNAME, password: CM_MONGO_ROOT_PASSWORD, roles: "root", database: "admin" },
+ {username: MONGO_READ_WRITE_USER, password: MONGO_READ_WRITE_PASS, permissions: [{role: "readWrite", database: MONGO_DB_NAME}]},
+ {username: MONGO_READ_USER, password: MONGO_READ_PASS, permissions: [{role: "read", database: MONGO_DB_NAME}]},
+ {username: MONGO_EXPORTER_USERNAME, password: MONGO_EXPORTER_PASSWORD, permissions: [{role: "clusterMonitor", database: "admin"}, {role: "read", database: MONGO_DB_NAME}]}
+];
+
+console.log("\n\n\n\nMONGO_READ_WRITE_USER: " + MONGO_READ_WRITE_USER);
+console.log("MONGO_READ_WRITE_PASS: " + MONGO_READ_WRITE_PASS + "\n\n\n\n");
- var config = {
- "_id": "rs0",
- "version": 1,
- "members": [
- {
- "_id": 0,
- "host": "mongo:27017",
- "priority": 2
- },
- ]
- };
- rs.initiate(config, { force: true });
- rs.status();
-} catch(e) {
- rs.status().ok
-}
// name -> collection name
// ttlField -> field to perform ttl on
// timeField -> field to index for time queries
+// intersectionField -> field containing intersection id for id queries
+// rsuIP -> field containing an rsuIP if available
+// expireTime -> the number of seconds after the ttl field at which the record should be deleted
+const odeCollections = [
+ // ODE Json data
+ {name: "OdeDriverAlertJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp", expireTime: expireSeconds},
+ {name: "OdeBsmJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp", expireTime: expireSeconds},
+ {name: "OdeBsmJson", "timeField": "recordGeneratedAt", rsuIP:"metadata.originIp", expireTime: expireSeconds},
+ {name: "OdeMapJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp", expireTime: expireSeconds},
+ {name: "OdeMapJson", "timeField": "recordGeneratedAt", rsuIP:"metadata.originIp", expireTime: expireSeconds},
+ {name: "OdeSpatJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp", expireTime: expireSeconds},
+ {name: "OdeSpatRxJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp", expireTime: expireSeconds},
+ {name: "OdeSrmJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp", expireTime: expireSeconds},
+ {name: "OdeSrmJson", "timeField": "recordGeneratedAt", rsuIP:"metadata.originIp", expireTime: expireSeconds},
+ {name: "OdeSsmJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp", expireTime: expireSeconds},
+ {name: "OdeSsmJson", "timeField": "recordGeneratedAt", rsuIP:"metadata.originIp", expireTime: expireSeconds},
+ {name: "OdeTimJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp", expireTime: expireSeconds},
+ {name: "OdeTimJson", "timeField": "recordGeneratedAt", rsuIP:"metadata.originIp", expireTime: expireSeconds},
+ {name: "OdeTimBroadcastJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp", expireTime: expireSeconds},
+ {name: "OdeTIMCertExpirationTimeJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp", expireTime: expireSeconds},
-const collections = [
- {name: "OdeBsmJson", ttlField: "recordGeneratedAt", timeField: "metadata.odeReceivedAt"},
- {name: "OdeMapJson", ttlField: "recordGeneratedAt", timeField: "metadata.odeReceivedAt"},
- {name: "OdeSpatJson", ttlField: "recordGeneratedAt", timeField: "metadata.odeReceivedAt"},
- {name: "OdeTimJson", ttlField: "recordGeneratedAt", timeField: "metadata.odeReceivedAt"},
- {name: "OdePsmJson", ttlField: "recordGeneratedAt", timeField: "metadata.odeReceivedAt"},
+ // Ode Raw ASN
+ {name: "OdeRawEncodedBSMJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp", expireTime: ttlExpireSeconds},
+ {name: "OdeRawEncodedMAPJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp", expireTime: ttlExpireSeconds},
+ {name: "OdeRawEncodedSPATJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp", expireTime: ttlExpireSeconds},
+ {name: "OdeRawEncodedSRMJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp", expireTime: ttlExpireSeconds},
+ {name: "OdeRawEncodedSSMJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp", expireTime: ttlExpireSeconds},
+ {name: "OdeRawEncodedTIMJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp", expireTime: ttlExpireSeconds},
];
-// Function to check if the replica set is ready
-function isReplicaSetReady() {
- let status;
- try {
- status = rs.status();
- } catch (error) {
- console.error("Error getting replica set status: " + error);
- return false;
- }
+// GeoJson Converter Data
+const geoJsonConverterCollections = [
+ {name: "ProcessedMap", ttlField: "recordGeneratedAt", timeField: "properties.timeStamp", intersectionField: "properties.intersectionId", expireTime: expireSeconds},
+ {name: "ProcessedSpat", ttlField: "recordGeneratedAt", timeField: "utcTimeStamp", intersectionField: "intersectionId", expireTime: expireSeconds},
+ {name: "ProcessedBsm", ttlField: "recordGeneratedAt", timeField: "timeStamp", geoSpatialField: "features.geometry.coordinates", expireTime: expireSeconds},
+];
- // Check if the replica set has a primary
- if (!status.hasOwnProperty('myState') || status.myState !== 1) {
- console.log("Replica set is not ready yet");
- return false;
- }
- console.log("Replica set is ready");
- return true;
+const conflictMonitorCollections = [
+ // Conflict Monitor Events
+ { name: "CmStopLineStopEvent", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "CmStopLinePassageEvent", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "CmIntersectionReferenceAlignmentEvents", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "CmSignalGroupAlignmentEvents", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "CmConnectionOfTravelEvent", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "CmSignalStateConflictEvents", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "CmLaneDirectionOfTravelEvent", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "CmSpatTimeChangeDetailsEvent", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "CmSpatMinimumDataEvents", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "CmMapBroadcastRateEvents", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "CmMapMinimumDataEvents", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "CmSpatBroadcastRateEvents", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "CmBsmEvents", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+
+
+ { name: "CmSpatMessageCountProgressionEvents", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "CmMapMessageCountProgressionEvents", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "CmBsmMessageCountProgressionEvents", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+
+ { name: "SpatMinimumDataAggregation", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "MapMinimumDataAggregation", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "IntersectionReferenceAlignmentAggregation", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "SignalGroupAlignmentAggregation", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "SignalStateConflictAggregation", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "TimeChangeDetailsAggregation", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "EventStateProgressionAggregation", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "BsmMessageCountProgressionAggregation", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "MapMessageCountProgressionAggregation", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "SpatMessageCountProgressionAggregation", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+
+
+ // Conflict Monitor Assessments
+ { name: "CmLaneDirectionOfTravelAssessment", ttlField: "assessmentGeneratedAt", timeField: "assessmentGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "CmConnectionOfTravelAssessment", ttlField: "assessmentGeneratedAt", timeField: "assessmentGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "CmSignalStateEventAssessment", ttlField: "assessmentGeneratedAt", timeField: "assessmentGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "CmStopLineStopAssessment", ttlField: "assessmentGeneratedAt", timeField: "assessmentGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+
+ // Conflict Monitor Notifications
+ { name: "CmSpatTimeChangeDetailsNotification", ttlField: "notificationGeneratedAt", timeField: "notificationGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "CmLaneDirectionOfTravelNotification", ttlField: "notificationGeneratedAt", timeField: "notificationGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "CmConnectionOfTravelNotification", ttlField: "notificationGeneratedAt", timeField: "notificationGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "CmAppHealthNotifications", ttlField: "notificationGeneratedAt", timeField: "notificationGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "CmSignalStateConflictNotification", ttlField: "notificationGeneratedAt", timeField: "notificationGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "CmSignalGroupAlignmentNotification", ttlField: "notificationGeneratedAt", timeField: "notificationGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "CmStopLinePassageNotification", ttlField: "notificationGeneratedAt", timeField: "notificationGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "CmStopLineStopNotification", ttlField: "notificationGeneratedAt", timeField: "notificationGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "CmNotification", ttlField: "notificationGeneratedAt", timeField: "notificationGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+
+ { name: "EventStateProgressionNotificationAggregation", ttlField: "notificationGeneratedAt", timeField: "notificationGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "IntersectionReferenceAlignmentNotificationAggregation", ttlField: "notificationGeneratedAt", timeField: "notificationGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "SignalGroupAlignmentNotificationAggregation", ttlField: "notificationGeneratedAt", timeField: "notificationGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "SignalStateConflictNotificationAggregation", ttlField: "notificationGeneratedAt", timeField: "notificationGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds },
+ { name: "TimeChangeDetailsNotificationAggregation", ttlField: "notificationGeneratedAt", timeField: "notificationGeneratedAt", intersectionField: "intersectionID", expireTime: expireSeconds }
+
+];
+
+let collections = [];
+
+if(CONNECT_CREATE_ODE){
+ collections = collections.concat(odeCollections);
}
-try{
+if(CONNECT_CREATE_GEOJSONCONVERTER){
+ collections = collections.concat(geoJsonConverterCollections);
+}
- // Wait for the replica set to be ready
- while (!isReplicaSetReady()) {
- sleep(retry_milliseconds);
+if(CONNECT_CREATE_CONFLICTMONITOR){
+ collections = collections.concat(conflictMonitorCollections);
+}
+
+
+try{
+
+ db.getMongo().setReadPref("primaryPreferred");
+ db = db.getSiblingDB("admin");
+ db.runCommand({autoCompact: true});
+
+ // Create Users in Database
+ for(user of users){
+ createUser(user);
}
- sleep(retry_milliseconds);
- // creates another user
- console.log("Creating Read Write user...");
- admin = db.getSiblingDB("admin");
- // Check if user already exists
- var user = admin.getUser(rw_user);
- if (user == null) {
- admin.createUser(
- {
- user: rw_user,
- pwd: rw_pass,
- roles: [
- { role: "readWrite", db: ode_db },
- ]
- }
- );
+
+ db = db.getSiblingDB(MONGO_DB_NAME);
+ db.getMongo().setReadPref("primaryPreferred");
+ var isMaster = db.isMaster();
+ if (isMaster.primary) {
+ console.log("Connected to the primary replica set member.");
} else {
- console.log("User \"" + rw_user + "\" already exists.");
+ console.log("Not connected to the primary replica set member. Current node: " + isMaster.host);
}
-
-} catch (error) {
- print("Error connecting to the MongoDB instance: " + error);
+}
+catch(err){
+ console.log("Could not switch DB to Sibling DB");
+ console.log(err);
}
+
// Wait for the collections to exist in mongo before trying to create indexes on them
let missing_collection_count;
-const db = db.getSiblingDB(ode_db);
do {
try {
missing_collection_count = 0;
- const collection_names = db.getCollectionNames();
+ const collectionNames = db.getCollectionNames();
for (collection of collections) {
- console.log("Creating Indexes for Collection" + collection["name"]);
- // Create Collection if It doesn't exist
+ // Create Collection if it doesn't exist
let created = false;
- if(!collection_names.includes(collection.name)){
+ if(!collectionNames.includes(collection['name'])){
created = createCollection(collection);
- // created = true;
}else{
created = true;
}
if(created){
- if (collection.hasOwnProperty('ttlField') && collection.ttlField !== 'none') {
- createTTLIndex(collection);
- }
-
-
+ createTTLIndex(collection);
+ createTimeIntersectionIndex(collection);
+ createTimeRsuIpIndex(collection);
+ createGeoSpatialIndex(collection);
}else{
missing_collection_count++;
- console.log("Collection " + collection.name + " does not exist yet");
+ console.log("Collection " + collection['name'] + " does not exist yet");
}
}
if (missing_collection_count > 0) {
- print("Waiting on " + missing_collection_count + " collections to be created...will try again in " + retry_milliseconds + " ms");
- sleep(retry_milliseconds);
+ console.log("Waiting on " + missing_collection_count + " collections to be created...will try again in " + retryMilliseconds + " ms");
+ sleep(retryMilliseconds);
}
} catch (err) {
console.log("Error while setting up TTL indexes in collections");
console.log(rs.status());
console.error(err);
- sleep(retry_milliseconds);
+ sleep(retryMilliseconds);
}
} while (missing_collection_count > 0);
console.log("Finished Creating All TTL indexes");
+function createUser(user){
+ try{
+ console.log("Creating User: " + user['username'] + " with Permissions: " + JSON.stringify(user['permissions']));
+ db.createUser(
+ {
+ user: user['username'],
+ pwd: user['password'],
+ roles: user['permissions'].map(permission => ({
+ role: permission['role'],
+ db: permission['database']
+ }))
+ });
+ } catch (error) {
+ console.error("Error creating user: ", error);
+ }
+}
function createCollection(collection){
try {
- db.createCollection(collection.name);
+ db.createCollection(collection['name']);
return true;
} catch (err) {
console.log("Unable to Create Collection: " + collection.name);
@@ -155,43 +261,181 @@ function createCollection(collection){
// Create TTL Indexes
function createTTLIndex(collection) {
- if (ttlIndexExists(collection)) {
- console.log("TTL index already exists for " + collection.name);
+ if(collection.hasOwnProperty("ttlField") && collection['ttlField'] != null){
+ const ttlField = collection['ttlField'];
+ const collectionName = collection['name'];
+ const duration = collection['expireTime'];
+
+ let indexJson = {};
+ indexJson[ttlField] = 1;
+
+
+ console.log(collectionName, duration, ttlField);
+ try{
+ if (ttlIndexExists(collection)) {
+ db.runCommand({
+ "collMod": collectionName,
+ "index": {
+ keyPattern: indexJson,
+ expireAfterSeconds: duration
+ }
+ });
+ console.log("Updated TTL index for " + collectionName + " using the field: " + ttlField + " as the timestamp");
+ }else{
+ db.getCollection(collectionName).createIndex(indexJson,
+ {expireAfterSeconds: duration}
+ );
+ console.log("Created TTL index for " + collectionName + " using the field: " + ttlField + " as the timestamp");
+ }
+ } catch(err){
+ console.log("Failed to Create or Update index for " + collectionName + " using the field: " + ttlField + " as the timestamp");
+ console.log(err);
+ }
+ }
+}
+
+function createTimeIndex(collection){
+ if(timeIndexExists(collection)){
+ // Skip if Index already Exists
return;
}
- const collection_name = collection.name;
- const timeField = collection.ttlField;
+ if(collection.hasOwnProperty("timeField") && collection['timeField'] != null){
+ const collectionName = collection['name'];
+ const timeField = collection['timeField'];
+ console.log("Creating Time Index for " + collectionName);
- console.log(
- "Creating TTL index for " + collection_name + " to remove documents after " +
- expire_seconds +
- " seconds"
- );
+ var indexJson = {};
+ indexJson[timeField] = -1;
- try {
- var index_json = {};
- index_json[timeField] = 1;
- db[collection_name].createIndex(index_json,
- {expireAfterSeconds: expire_seconds}
- );
- console.log("Created TTL index for " + collection_name + " using the field: " + timeField + " as the timestamp");
- } catch (err) {
- var pattern_json = {};
- pattern_json[timeField] = 1;
- db.runCommand({
- "collMod": collection_name,
- "index": {
- keyPattern: pattern_json,
- expireAfterSeconds: expire_seconds
- }
- });
- console.log("Updated TTL index for " + collection_name + " using the field: " + timeField + " as the timestamp");
+ try {
+ db[collectionName].createIndex(indexJson);
+ console.log("Created Time Intersection index for " + collectionName + " using the field: " + timeField + " as the timestamp");
+ } catch (err) {
+ db.runCommand({
+ "collMod": collectionName,
+ "index": {
+ keyPattern: indexJson
+ }
+ });
+ console.log("Updated Time index for " + collectionName + " using the field: " + timeField + " as the timestamp");
+ }
+ }
+}
+
+function createTimeRsuIpIndex(){
+ if(timeRsuIpIndexExists(collection)){
+ // Skip if Index already Exists
+ return;
+ }
+
+ if(collection.hasOwnProperty("timeField") && collection.timeField != null && collection.hasOwnProperty("rsuIP") && collection.rsuIP != null){
+ const collectionName = collection['name'];
+ const timeField = collection['timeField'];
+ const rsuIP = collection['rsuIP'];
+ console.log("Creating Time rsuIP Index for " + collectionName);
+
+ var indexJson = {};
+ indexJson[rsuIP] = -1;
+ indexJson[timeField] = -1;
+
+
+ try {
+ db[collectionName].createIndex(indexJson);
+ console.log("Created Time rsuIP Intersection index for " + collectionName + " using the field: " + timeField + " as the timestamp and : " + rsuIP+" as the rsuIP");
+ } catch (err) {
+ db.runCommand({
+ "collMod": collectionName,
+ "index": {
+ keyPattern: indexJson
+ }
+ });
+ console.log("Updated Time rsuIP index for " + collectionName + " using the field: " + timeField + " as the timestamp and : " + rsuIP+" as the rsuIP");
+ }
+ }
+}
+
+
+function createTimeIntersectionIndex(collection){
+ if(timeIntersectionIndexExists(collection)){
+ // Skip if Index already Exists
+ return;
}
+ if(collection.hasOwnProperty("timeField") && collection.timeField != null && collection.hasOwnProperty("intersectionField") && collection.intersectionField != null){
+ const collectionName = collection['name'];
+ const timeField = collection['timeField'];
+ const intersectionField = collection['intersectionField'];
+ console.log("Creating time intersection index for " + collectionName);
+
+ var indexJson = {};
+ indexJson[intersectionField] = -1;
+ indexJson[timeField] = -1;
+
+
+ try {
+ db[collectionName].createIndex(indexJson);
+ console.log("Created time intersection index for " + collectionName + " using the field: " + timeField + " as the timestamp and : " + intersectionField + " as the rsuIP");
+ } catch (err) {
+ db.runCommand({
+ "collMod": collectionName,
+ "index": {
+ keyPattern: indexJson
+ }
+ });
+ console.log("Updated time intersection index for " + collectionName + " using the field: " + timeField + " as the timestamp and : " + intersectionField + " as the rsuIP");
+ }
+ }
}
+function createGeoSpatialIndex(collection){
+ if(geoSpatialIndexExists(collection)){
+ return;
+ }
+
+ if(collection.hasOwnProperty("timeField") && collection['timeField'] != null && collection.hasOwnProperty("geoSpatialField") && collection['geoSpatialField'] != null){
+ const collectionName = collection['name'];
+ const timeField = collection['timeField'];
+ const geoSpatialField = collection['geoSpatialField'];
+ console.log("Creating GeoSpatial index for " + collectionName);
+
+ var indexJson = {};
+ indexJson[geoSpatialField] = "2dsphere";
+ indexJson[timeField] = -1;
+
+
+ try {
+ db[collectionName].createIndex(indexJson);
+ console.log("Created time geospatial index for " + collectionName + " using the field: " + timeField + " as the timestamp and : " + geoSpatialField + " as the GeoSpatial Field");
+ } catch (err) {
+ db.runCommand({
+ "collMod": collectionName,
+ "index": {
+ keyPattern: indexJson
+ }
+ });
+ console.log("Updated time geospatial index for " + collectionName + " using the field: " + timeField + " as the timestamp and : " + geoSpatialField + " as the GeoSpatial Field");
+ }
+ }
+
+}
function ttlIndexExists(collection) {
- return db[collection.name].getIndexes().find((idx) => idx.hasOwnProperty('expireAfterSeconds')) !== undefined;
-}
\ No newline at end of file
+ return db[collection['name']].getIndexes().find((idx) => idx.hasOwnProperty("expireAfterSeconds")) !== undefined;
+}
+
+function timeIntersectionIndexExists(collection){
+ return db[collection['name']].getIndexes().find((idx) => idx.name == collection['intersectionField'] + "_-1_" + collection['timeField'] + "_-1") !== undefined;
+}
+
+function timeRsuIpIndexExists(collection){
+ return db[collection['name']].getIndexes().find((idx) => idx.name == collection['rsuIP'] + "_-1_" + collection['timeField'] + "_-1") !== undefined;
+}
+
+function timeIndexExists(collection){
+ return db[collection['name']].getIndexes().find((idx) => idx.name == collection['timeField'] + "_-1") !== undefined;
+}
+
+function geoSpatialIndexExists(collection){
+ return db[collection['name']].getIndexes().find((idx) => idx.name == collection['geoSpatialField'] + "_2dsphere_timeStamp_-1") !== undefined;
+}
diff --git a/mongo/init_replicas.js b/mongo/init_replicas.js
new file mode 100644
index 0000000..9578377
--- /dev/null
+++ b/mongo/init_replicas.js
@@ -0,0 +1,33 @@
+
+/*
+
+This script is the first of two scripts responsible for setting up mongoDB on initial boot.
+These scripts should be copied to the /docker-entrypoint-initdb.d/ directory within the docker image;
+the docker image will then execute these scripts automatically when the database is first created.
+This script and is partner are prefixed with the letters a and b respectively to ensure they are run
+in the proper order when copied into the mongoDB docker image.
+
+Since the conflict monitor uses a replica set in its mongoDB configuration. Initializing the replica set
+and configuring the collections are separated from one another. The purpose of this to force a reconnect
+to the database. This in turn allows the new connection to be connected to the primary replica which is
+required for creating indexes on all the collections. This script is only responsible for creating the
+replica set. Almost all other configuration should go in b_create_indexes.js
+
+Documentation on how the mongoDB docker image runs startup scripts can be found here
+https://hub.docker.com/_/mongo/
+
+*/
+
+
+
+console.log("Initializing Replicas");
+try{
+ db_status = rs.status();
+} catch(err){
+ console.log("Initializing New DB");
+ try{
+ rs.initiate({ _id: 'rs0', members: [{ _id: 0, host: 'localhost:27017' }] }).ok
+ }catch(err){
+ console.log("Unable to Initialize DB");
+ }
+}
diff --git a/mongo/manage-volume-cron b/mongo/manage-volume-cron
new file mode 100644
index 0000000..57ca0fc
--- /dev/null
+++ b/mongo/manage-volume-cron
@@ -0,0 +1,12 @@
+MONGO_DATABASE_NAME=${MONGO_DB_NAME}
+MONGO_DATABASE_STORAGE_COLLECTION_NAME=${MONGO_DATABASE_STORAGE_COLLECTION_NAME}
+MONGO_DATABASE_SIZE_GB=${MONGO_DATABASE_SIZE_GB}
+MONGO_DATABASE_SIZE_TARGET_PERCENT=${MONGO_DATABASE_SIZE_TARGET_PERCENT}
+MONGO_DATABASE_DELETE_THRESHOLD_PERCENT=${MONGO_DATABASE_DELETE_THRESHOLD_PERCENT}
+MONGO_DATABASE_MAX_TTL_RETENTION_SECONDS=${MONGO_DATABASE_MAX_TTL_RETENTION_SECONDS}
+MONGO_DATABASE_MIN_TTL_RETENTION_SECONDS=${MONGO_DATABASE_MIN_TTL_RETENTION_SECONDS}
+MONGO_INITDB_ROOT_USERNAME=${MONGO_INITDB_ROOT_USERNAME}
+MONGO_INITDB_ROOT_PASSWORD=${MONGO_INITDB_ROOT_PASSWORD}
+
+* * * * * root mongosh /data/manage_volume.js > /var/log/cron.log 2>&1
+# An empty line is required at the end of this file for a valid cron file.
\ No newline at end of file
diff --git a/mongo/manage_volume.js b/mongo/manage_volume.js
new file mode 100644
index 0000000..899caa8
--- /dev/null
+++ b/mongo/manage_volume.js
@@ -0,0 +1,250 @@
+
+// Mongo Data Managment Script
+
+// Features
+// Automatically Logs Collection Sizes
+// Automatically Updates Data Retention Periods to prevent overflow
+// Performs Emergency Record Deletion when Collections get to large
+
+// Database to Perform operation on.
+const MONGO_DATABASE_NAME = process.env.MONGO_DATABASE_NAME || "CV";
+
+// The name of the collection to store the data as.
+const MONGO_DATABASE_STORAGE_COLLECTION_NAME = process.env.MONGO_DATABASE_STORAGE_COLLECTION_NAME || "MongoStorage";
+
+// Total Size of the database disk in GB. This script will work to ensure all data fits within this store.
+const MONGO_DATABASE_SIZE_GB = process.env.MONGO_DATABASE_SIZE_GB || 1000;
+
+// Specified as a percent of total database size. This is the storage target the database will try to hit.
+const MONGO_DATABASE_SIZE_TARGET_PERCENT = process.env.MONGO_DATABASE_SIZE_TARGET_PERCENT || 0.8;
+
+// Specified as a percent of total database size.
+const MONGO_DATABASE_DELETE_THRESHOLD_PERCENT = process.env.MONGO_DATABASE_DELETE_THRESHOLD_PERCENT || 0.9;
+
+// The maximum amount of time data should be retained. Measured in Seconds.
+const MONGO_DATABASE_MAX_TTL_RETENTION_SECONDS = process.env.MONGO_DATABASE_MAX_TTL_RETENTION_SECONDS || 5184000; // 60 Days
+
+// The minimum amount of time data should be retained. Measured in Seconds. This only effects TTL's set on the data. It will not prevent the database from manual data deletion.
+const MONGO_DATABASE_MIN_TTL_RETENTION_SECONDS = process.env.MONGO_DATABASE_MIN_TTL_RETENTION_SECONDS || 604800; // 7 Days
+
+
+const MONGO_ROOT_USERNAME = process.env.MONGO_INITDB_ROOT_USERNAME || "root";
+const MONGO_ROOT_PASSWORD = process.env.MONGO_INITDB_ROOT_PASSWORD || "root";
+
+const MONGO_ENABLE_STORAGE_RECORD = process.env.MONGO_ENABLE_STORAGE_RECORD || true;
+const MONGO_ENABLE_DYNAMIC_TTL = (process.env.MONGO_ENABLE_DYNAMIC_TTL || true) && MONGO_ENABLE_STORAGE_RECORD; //Storage record must be enabled for Dynamic TTL
+
+
+const MS_PER_HOUR = 60 * 60 * 1000;
+const BYTE_TO_GB = 1024 * 1024 * 1024;
+const DB_TARGET_SIZE_BYTES = MONGO_DATABASE_SIZE_GB * MONGO_DATABASE_SIZE_TARGET_PERCENT * BYTE_TO_GB;
+const DB_DELETE_SIZE_BYETS = MONGO_DATABASE_SIZE_GB * MONGO_DATABASE_DELETE_THRESHOLD_PERCENT * BYTE_TO_GB;
+
+
+
+
+print("Managing Mongo Data Volumes");
+
+db = db.getSiblingDB("admin");
+db.auth(MONGO_ROOT_USERNAME, MONGO_ROOT_PASSWORD);
+db = db.getSiblingDB(MONGO_DATABASE_NAME);
+
+class CollectionStats{
+ constructor(name, allocatedSpace, freeSpace, indexSpace){
+ this.name = name;
+ this.allocatedSpace = allocatedSpace;
+ this.freeSpace = freeSpace;
+ this.indexSize = indexSpace;
+ }
+}
+
+
+class StorageRecord{
+ constructor(collectionStats, totalAllocatedStorage, totalFreeSpace, totalIndexSize){
+ this.collectionStats = collectionStats;
+ this.recordGeneratedAt = ISODate();
+ this.totalAllocatedStorage = totalAllocatedStorage;
+ this.totalFreeSpace = totalFreeSpace;
+ this.totalIndexSize = totalIndexSize;
+ this.totalSize = totalAllocatedStorage + totalFreeSpace + totalIndexSize;
+ }
+}
+
+function ema_deltas(records){
+ const a = 0.5;
+ let average_delta = 0;
+
+ for(let i=0; i< records.length-1; i++){
+ const delta = records[i+1] - records[i];
+ average_delta += Math.pow(a, records.length -i -1) * delta;
+ }
+
+ return average_delta;
+
+}
+
+function updateTTL(){
+
+ print("Updating TTL")
+ const ttl = getLatestTTL();
+ if(ttl == 0){
+ print("Skipping TTL Update")
+ // Do not update TTL's
+ return;
+ }
+
+
+ const newestRecords = db.getCollection(MONGO_DATABASE_STORAGE_COLLECTION_NAME).find().sort({"recordGeneratedAt":-1}).limit(10);
+
+ let sizes = [];
+ newestRecords.forEach(doc => {
+ let total = 0;
+ for(let i=0; i < doc.collectionStats.length; i++){
+ total += doc.collectionStats[i].allocatedSpace + doc.collectionStats[i].freeSpace + doc.collectionStats[i].indexSize;
+ }
+
+ sizes.push(total);
+ });
+
+
+ // Overshoot Prevention
+ const growth = ema_deltas(sizes);
+ const oldestSpat = db.getCollection("ProcessedSpat").find().sort({"recordGeneratedAt":1}).limit(1);
+
+ let new_ttl = ttl;
+ let possible_ttl = ttl;
+
+ // Check if collection is still growing to capacity, or if it in steady state
+ if(oldestSpat.recordGeneratedAt > ISODate() - ttl + MS_PER_HOUR && growth > 0){
+ possible_ttl = DB_TARGET_SIZE_BYTES / growth;
+ }else{
+ possible_ttl = 3600 * ((DB_TARGET_SIZE_BYTES - sizes[0])/BYTE_TO_GB) + ttl; // Shift the TTL by roughly 1 hour for every GB of data over or under
+ }
+
+ // Clamp TTL and assign to new TTL;
+
+ if(!isNaN(possible_ttl) && possible_ttl != 0){
+ if(possible_ttl > MONGO_DATABASE_MAX_TTL_RETENTION_SECONDS){
+ new_ttl = MONGO_DATABASE_MAX_TTL_RETENTION_SECONDS;
+ }else if(possible_ttl < MONGO_DATABASE_MIN_TTL_RETENTION_SECONDS){
+ new_ttl = MONGO_DATABASE_MIN_TTL_RETENTION_SECONDS;
+ }else{
+ new_ttl = Math.round(possible_ttl);
+ }
+ new_ttl = Number(new_ttl);
+ print("Calculated New TTL for MongoDB: " + new_ttl);
+ applyNewTTL(new_ttl);
+ }else{
+ print("Not Updating TTL New TTL is NaN");
+ }
+}
+
+function getLatestTTL(){
+ const indexes = db.getCollection("ProcessedSpat").getIndexes();
+ for (let i=0; i < indexes.length; i++){
+ if(indexes[i].hasOwnProperty("expireAfterSeconds")){
+ return indexes[i]["expireAfterSeconds"];
+ }
+ }
+ return 0;
+}
+
+function getTTLKey(collection){
+ const indexes = db.getCollection(collection).getIndexes();
+ for (let i=0; i < indexes.length; i++){
+ if(indexes[i].hasOwnProperty("expireAfterSeconds")){
+ return [indexes[i]["key"], indexes[i]["expireAfterSeconds"]];
+ }
+ }
+ return [null, null];
+}
+
+function applyNewTTL(ttl){
+ var collections = db.getCollectionNames();
+ for(let i=0; i< collections.length; i++){
+ const collection = collections[i];
+ let [key, oldTTL] = getTTLKey(collection);
+ if(oldTTL != ttl && key != null){
+ print("Updating TTL For Collection: " + collection, ttl);
+ db.runCommand({
+ "collMod": collection,
+ "index": {
+ keyPattern: key,
+ expireAfterSeconds: ttl
+ }});
+ }
+ }
+}
+
+
+function addNewStorageRecord(){
+ var collections = db.getCollectionNames();
+ let totalAllocatedStorage = 0;
+ let totalFreeSpace = 0;
+ let totalIndexSize = 0;
+
+ let records = [];
+
+ for (var i = 0; i < collections.length; i++) {
+ let stats = db.getCollection(collections[i]).stats();
+ let colStats = db.runCommand({"collstats": collections[i]});
+ let blockManager = colStats["wiredTiger"]["block-manager"];
+
+ let freeSpace = Number(blockManager["file bytes available for reuse"]);
+ let allocatedStorage = Number(blockManager["file size in bytes"]);
+ let indexSize = Number(stats.totalIndexSize);
+
+ records.push(new CollectionStats(collections[i], allocatedStorage, freeSpace, indexSize));
+
+ totalAllocatedStorage += allocatedStorage
+ totalFreeSpace += freeSpace;
+ totalIndexSize += indexSize;
+
+ print(collections[i], allocatedStorage / BYTE_TO_GB, freeSpace/ BYTE_TO_GB, indexSize / BYTE_TO_GB);
+ }
+
+ const storageRecord = new StorageRecord(records, totalAllocatedStorage, totalFreeSpace, totalIndexSize);
+ db.getCollection(MONGO_DATABASE_STORAGE_COLLECTION_NAME).insertOne(storageRecord);
+}
+
+function compactCollections(){
+ print("Checking Collection Compaction");
+
+ var collections = db.getCollectionNames();
+
+ let activeCompactions = [];
+ db.currentOp({ "active": true, "secs_running": { "$gt": 0 } }).inprog.forEach(op => {
+ if (op.msg && op.msg.includes("compact")) {
+ print("Found Active Compactions");
+ activeCompactions.push(op.command.compact);
+ }
+ });
+
+ for (var i = 0; i < collections.length; i++) {
+ let colStats = db.runCommand({"collstats": collections[i]});
+ let blockManager = colStats["wiredTiger"]["block-manager"];
+
+ let freeSpace = Number(blockManager["file bytes available for reuse"]);
+ let allocatedStorage = Number(blockManager["file size in bytes"]);
+
+ // If free space makes up a significant proportion of allocated storage
+ if(freeSpace > allocatedStorage * 0.5 && allocatedStorage > (1 * BYTE_TO_GB)){
+ if(!activeCompactions.includes(collections[i])){
+ print("Compacting Collection", collections[i]);
+ db.runCommand({compact: collections[i], force:true});
+ }else{
+ print("Skipping Compaction, Collection Compaction is already scheduled");
+ }
+ }
+ }
+}
+
+if(MONGO_ENABLE_STORAGE_RECORD){
+ addNewStorageRecord();
+}
+
+if(MONGO_ENABLE_DYNAMIC_TTL){
+ updateTTL();
+}
+
+
diff --git a/mongo/setup_mongo.sh b/mongo/setup_mongo.sh
old mode 100644
new mode 100755
index 13cb4e7..c6f0a77
--- a/mongo/setup_mongo.sh
+++ b/mongo/setup_mongo.sh
@@ -8,4 +8,5 @@ echo "MongoDB is up and running!"
cd /
-mongosh -u $MONGO_ADMIN_DB_USER -p $MONGO_ADMIN_DB_PASS --authenticationDatabase admin --host mongo:27017 /create_indexes.js
+mongosh -u $MONGO_ADMIN_DB_USER -p $MONGO_ADMIN_DB_PASS --authenticationDatabase admin --host mongo:27017 /init_replicas.js
+mongosh -u $MONGO_ADMIN_DB_USER -p $MONGO_ADMIN_DB_PASS --authenticationDatabase admin --host mongo:27017 /create_indexes.js
diff --git a/sample.env b/sample.env
index 3f471bd..797d2aa 100644
--- a/sample.env
+++ b/sample.env
@@ -3,6 +3,7 @@
# Hint: look for "inet addr:" within "eth0" or "en0" for OSX
DOCKER_HOST_IP=""
+
# Docker compose restart policy: https://docs.docker.com/engine/containers/start-containers-automatically/
RESTART_POLICY="on-failure:3"
@@ -20,12 +21,20 @@ RESTART_POLICY="on-failure:3"
# - kafka
# - mongo
# - kafka_connect
+# - kafka-connect-setup
# - kafka_connect
# - kafka_connect
+# - kafka-connect-setup
+# - deduplicator
+# - deduplicator
# EXAMPLE: COMPOSE_PROFILES=kafka_connect_standalone,kafka_ui,mongo_express
COMPOSE_PROFILES=all
+
+
### COMMON variables - END ###
+
+
### KAFKA variables - START ###
KAFKA_BOOTSTRAP_SERVERS=${DOCKER_HOST_IP}:9092
KAFKA_LOG_RETENTION_HOURS=3
@@ -41,12 +50,19 @@ KAFKA_TOPIC_CREATE_ODE=true # Create topics for ODE
KAFKA_TOPIC_CREATE_GEOJSONCONVERTER=true # Create topics for GeoJSON Converter
KAFKA_TOPIC_CREATE_CONFLICTMONITOR=true # Create topics for Conflict Monitor
KAFKA_TOPIC_CREATE_DEDUPLICATOR=true # Create topics for Deduplicator
+# Relative path to the Kafka topic yaml configuration script, upper level directories are supported
+# NOTE: This script is used to create kafka topics
+KAFKA_TOPIC_CONFIG_RELATIVE_PATH="./jikkou/kafka-topics-values.yaml"
### KAFKA variables - END ###
### MONGODB variables - START ###
# NOTE: Must set a password for the container to start up properly
MONGO_IP=${DOCKER_HOST_IP}
-MONGO_DB_NAME=ode
+MONGO_DB_NAME=CV
+
+# Generate a random string for the MongoDB keyfile using the following command:
+# $ openssl rand -base64 32
+MONGO_DB_KEYFILE_STRING=
MONGO_ADMIN_DB_USER=admin
MONGO_ADMIN_DB_PASS=
@@ -54,23 +70,76 @@ MONGO_ADMIN_DB_PASS=
MONGO_READ_WRITE_USER=ode
MONGO_READ_WRITE_PASS=
-MONGO_PORT=27017
-MONGO_URI=mongodb://${MONGO_READ_WRITE_USER}:${MONGO_READ_WRITE_PASS}@${MONGO_IP}:${MONGO_PORT}/?directConnection=true
-MONGO_COLLECTION_TTL=7 # days
+MONGO_READ_USER=user
+MONGO_READ_PASS=
+
+MONGO_EXPORTER_USERNAME=export
+MONGO_EXPORTER_PASSWORD=
MONGO_EXPRESS_USER=${MONGO_ADMIN_DB_USER}
MONGO_EXPRESS_PASS=${MONGO_ADMIN_DB_PASS}
+MONGO_PORT=27017
+MONGO_DATA_RETENTION_SECONDS=5184000
+MONGO_ASN_RETENTION_SECONDS=86400
+
+
+MONGO_DATABASE_STORAGE_COLLECTION_NAME=MongoStorage
+MONGO_DATABASE_SIZE_GB=1000
+MONGO_DATABASE_SIZE_TARGET_PERCENT=0.8
+MONGO_DATABASE_DELETE_THRESHOLD_PERCENT=0.9
+MONGO_DATABASE_MAX_TTL_RETENTION_SECONDS=5184000
+MONGO_DATABASE_MIN_TTL_RETENTION_SECONDS=604800
+MONGO_DATABASE_COMPACTION_TRIGGER_PERCENT=MONGO_DATABASE_COMPACTION_TRIGGER_PERCENT
+MONGO_ENABLE_STORAGE_RECORD=true
+MONGO_ENABLE_DYNAMIC_TTL=true
+
+
+
# Relative path to the MongoDB init script, upper level directories are supported
MONGO_SETUP_SCRIPT_RELATIVE_PATH="./mongo/setup_mongo.sh"
+MONGO_INIT_REPLICAS_SCRIPT_RELATIVE_PATH="./mongo/init_replicas.js"
MONGO_CREATE_INDEXES_SCRIPT_RELATIVE_PATH="./mongo/create_indexes.js"
+MONGO_MANAGE_VOLUMES_SCRIPT_RELATIVE_PATH="./mongo/manage_volume.js"
+
+
+
### MONGODB variables - END ###
### Kafka connect variables - START ###
# NOTE: Required variables: [MONGODB, KAFKA]
+CONNECT_URL=http://${DOCKER_HOST_IP}:8083
# Kafka connect log level
CONNECT_LOG_LEVEL=ERROR
-# Relative path to the Kafka init script, upper level directories are supported
+
+CONNECT_TASKS_MAX=1 # Number of concurrent tasks to configure on kafka connectors
+CONNECT_CREATE_ODE=true # Create kafka connectors to MongoDB for ODE
+CONNECT_CREATE_GEOJSONCONVERTER=true # Create kafka connectors to MongoDB for GeoJSON Converter
+CONNECT_CREATE_CONFLICTMONITOR=true # Create kafka connectors to MongoDB for Conflict Monitor
+CONNECT_CREATE_DEDUPLICATOR=true # Create kafka connectors to MongoDB for Deduplicator
+# Relative path to the Kafka Connector yaml configuration script, upper level directories are supported
# NOTE: This script is used to create kafka connectors
-CONNECT_SCRIPT_RELATIVE_PATH="./kafka-connect/connect_start.sh"
-### Kafka connect variables - END ###
\ No newline at end of file
+CONNECT_CONFIG_RELATIVE_PATH="./jikkou/kafka-connectors-values.yaml"
+
+### Kafka connect variables - END ###
+
+### DEDUPLICATOR variables - START ###
+
+# Required for building the deduplicator. Documentation: https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-apache-maven-registry#authenticating-to-github-packages
+MAVEN_GITHUB_TOKEN=
+MAVEN_GITHUB_ORG=usdot-jpo-ode
+
+ENABLE_PROCESSED_MAP_DEDUPLICATION=${KAFKA_TOPIC_CREATE_GEOJSONCONVERTER}
+ENABLE_PROCESSED_MAP_WKT_DEDUPLICATION=${KAFKA_TOPIC_CREATE_GEOJSONCONVERTER}
+ENABLE_ODE_MAP_DEDUPLICATION=${KAFKA_TOPIC_CREATE_ODE}
+ENABLE_ODE_TIM_DEDUPLICATION=${KAFKA_TOPIC_CREATE_ODE}
+ENABLE_ODE_RAW_ENCODED_TIM_DEDUPLICATION=${KAFKA_TOPIC_CREATE_ODE}
+ENABLE_PROCESSED_SPAT_DEDUPLICATION=${KAFKA_TOPIC_CREATE_GEOJSONCONVERTER}
+ENABLE_ODE_BSM_DEDUPLICATION=${KAFKA_TOPIC_CREATE_ODE}
+
+
+
+### DEDUPLICATOR variables - END ###
+
+
+