From 79d96b87518dfdb743603c063fc32ea49b71abaf Mon Sep 17 00:00:00 2001 From: John-Wiens Date: Tue, 7 May 2024 10:06:28 -0600 Subject: [PATCH 01/29] Adding Mock Decoder for ASN.1 --- api/jpo-conflictvisualizer-api/pom.xml | 5 + .../ode/api/controllers/ConfigController.java | 5 - .../api/controllers/DecoderController.java | 100 ++++++++++++++++++ .../its/jpo/ode/api/models/UploadData.java | 24 +++++ .../ode/mockdata/MockUploadDataGenerator.java | 25 +++++ 5 files changed, 154 insertions(+), 5 deletions(-) create mode 100644 api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/controllers/DecoderController.java create mode 100644 api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/UploadData.java create mode 100644 api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockUploadDataGenerator.java diff --git a/api/jpo-conflictvisualizer-api/pom.xml b/api/jpo-conflictvisualizer-api/pom.xml index 351328a3..24e782d3 100644 --- a/api/jpo-conflictvisualizer-api/pom.xml +++ b/api/jpo-conflictvisualizer-api/pom.xml @@ -105,6 +105,11 @@ jpo-ode-plugins 2.0.0-SNAPSHOT + + usdot.jpo.ode + jpo-ode-svcs + 2.0.0-SNAPSHOT + usdot.jpo.ode jpo-geojsonconverter diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/controllers/ConfigController.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/controllers/ConfigController.java index 2904942f..2db14d76 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/controllers/ConfigController.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/controllers/ConfigController.java @@ -6,7 +6,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; import org.springframework.data.mongodb.core.query.Query; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -14,7 +13,6 @@ import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @@ -25,11 +23,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; -import scala.collection.generic.BitOperations.Long; - import org.springframework.web.bind.annotation.RequestBody; -import us.dot.its.jpo.conflictmonitor.monitor.algorithms.config.ConfigUpdateResult; import us.dot.its.jpo.conflictmonitor.monitor.models.config.Config; import us.dot.its.jpo.conflictmonitor.monitor.models.config.DefaultConfig; import us.dot.its.jpo.conflictmonitor.monitor.models.config.DefaultConfigMap; diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/controllers/DecoderController.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/controllers/DecoderController.java new file mode 100644 index 00000000..10870d6c --- /dev/null +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/controllers/DecoderController.java @@ -0,0 +1,100 @@ +package us.dot.its.jpo.ode.api.controllers; + +import java.time.ZonedDateTime; + + +// import jakarta.ws.rs.core.Response; + +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import com.fasterxml.jackson.databind.ObjectMapper; + +import us.dot.its.jpo.ode.api.models.UploadData; +import us.dot.its.jpo.ode.mockdata.MockUploadDataGenerator; + +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.ResponseBody; +import us.dot.its.jpo.ode.api.ConflictMonitorApiProperties; + +// import us.dot.its.jpo.ode.coder.StringPublisher; + +@RestController +public class DecoderController { + + private static final Logger logger = LoggerFactory.getLogger(AssessmentController.class); + + ObjectMapper objectMapper = new ObjectMapper(); + + @Autowired + ConflictMonitorApiProperties props; + + + // private StringPublisher bsmPublisher; + + public String getCurrentTime() { + return ZonedDateTime.now().toInstant().toEpochMilli() + ""; + } + + @CrossOrigin(origins = "http://localhost:3000") + @RequestMapping(value = "/decoder/upload", method = RequestMethod.POST, produces = "application/json") + public @ResponseBody ResponseEntity new_bulk_upload_request( + @RequestBody UploadData newUploadData, + @RequestParam(name = "test", required = false, defaultValue = "false") boolean testData) { + try { + logger.info("Uploading Bulk Data"); + + if (testData) { + newUploadData = MockUploadDataGenerator.getUploadData(); + } else { + return ResponseEntity.status(HttpStatus.NOT_IMPLEMENTED).contentType(MediaType.TEXT_PLAIN) + .body(newUploadData.toString()); + } + + return ResponseEntity.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON) + .body(newUploadData.toString()); + } catch (Exception e) { + logger.info("Failed to Upload Bulk Data"); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).contentType(MediaType.TEXT_PLAIN) + .body(ExceptionUtils.getStackTrace(e)); + } + } + + // public void publishBSM(String asn1Bsm){ + // String payloadHexString = HexUtils.toHexString(payload); + // logger.debug("Packet: {}", payloadHexString); + + // // Add header data for the decoding process + // ZonedDateTime utc = ZonedDateTime.now(ZoneOffset.UTC); + // String timestamp = utc.format(DateTimeFormatter.ISO_INSTANT); + + // JSONObject metadataObject = new JSONObject(); + // metadataObject.put("utctimestamp", timestamp); + // metadataObject.put("originRsu", senderIp); + + // JSONObject messageObject = new JSONObject(); + // messageObject.put("metadata", metadataObject); + // messageObject.put("payload", payloadHexString); + + // JSONArray messageList = new JSONArray(); + // messageList.put(messageObject); + + // JSONObject jsonObject = new JSONObject(); + // jsonObject.put("BsmMessageContent", messageList); + + // logger.debug("BSM JSON Object: {}", jsonObject.toString()); + + // // Submit JSON to the OdeRawEncodedMessageJson Kafka Topic + // this.bsmPublisher.publish(jsonObject.toString(), this.bsmPublisher.getOdeProperties().getKafkaTopicOdeRawEncodedBSMJson()); + + // } +} \ No newline at end of file diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/UploadData.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/UploadData.java new file mode 100644 index 00000000..6a77ca54 --- /dev/null +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/UploadData.java @@ -0,0 +1,24 @@ +package us.dot.its.jpo.ode.api.models; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +import java.util.ArrayList; +import java.util.List; + +import lombok.EqualsAndHashCode; + +@ToString +@Setter +@EqualsAndHashCode +@Getter +public class UploadData { + public boolean test; + public List bsmList = new ArrayList<>(); + public List spatList = new ArrayList<>(); + public List mapList = new ArrayList<>(); + public List genericUpload = new ArrayList<>(); + public Long uploadTime; + public String ID; +} \ No newline at end of file diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockUploadDataGenerator.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockUploadDataGenerator.java new file mode 100644 index 00000000..bb9de865 --- /dev/null +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockUploadDataGenerator.java @@ -0,0 +1,25 @@ +package us.dot.its.jpo.ode.mockdata; + +import java.time.Instant; +import java.util.UUID; + +import us.dot.its.jpo.ode.api.models.UploadData; + +public class MockUploadDataGenerator { + + public static UploadData getUploadData(){ + + + UploadData data = new UploadData(); + + data.bsmList.add("0022e12d18466c65c1493800000e00e4616183e85a8f0100c000038081bc001480b8494c4c950cd8cde6e9651116579f22a424dd78fffff00761e4fd7eb7d07f7fff80005f11d1020214c1c0ffc7c016aff4017a0ff65403b0fd204c20ffccc04f8fe40c420ffe6404cefe60e9a10133408fcfde1438103ab4138f00e1eec1048ec160103e237410445c171104e26bc103dc4154305c2c84103b1c1c8f0a82f42103f34262d1123198103dac25fb12034ce10381c259f12038ca103574251b10e3b2210324c23ad0f23d8efffe0000209340d10000004264bf00"); + data.mapList.add("001283c138003000205e9c014d3eab092ca624b5518202dc3658042800000400023622c60ca009f66d48abfaf81388d8ad18070027d9b2ffcfe9804f13667b1ffd009ec2c76e3ffc82c4e0001004b00c5000000800066c4574101813ecd8b757fae027d9b30e6ff5604ec363561fe7809ec6cd69bfec813c4d8a617fc9027d9b2147008604fb163666000016250000802580228000001000096229e1309b51a6fe4204dd361cf1fe5009f6018e1000096020a00000080004d88a57f84027d9b3827002804ec36087600a009f62c289407282c310001c0440188800000006c46dbe02813ec5816d800710052200000001b11b6fad404fb16054a0000401c8800000006c47b3d24813ec5816d801b100c4200000000af890f12c580007e87100d4200000008af4c0f12c580077e7a2c0004000160002001cb028d000000800052c160bc40b5fffd8a9409d86bfebb5b40141457fef53b76c008b467014145800080002bffcbffc82c6a0001804b024d000000800036c2213c3b013ecd80096d64027d9affd8cdfc04f635ff7983bc09f66c0082aa2014280b1b80006012c0b3400000100004b02bcf0f6d7fe065d602788b0138eb900b1240001012c083400000080009b0c2af0b804fb15fe6de171afff6c63e04ec15fe1de670060e40002581ea8000004000135da6df0180a0a6adc2c00d0143cd51897fda028c8abb25001a0b0680008012c105400000200009aedbefae005053540ee003c0a326a9cf3fed8143c5667780010582c0004009608aa00000080004d76de7ee402829aba88ffdc050f354525fff80a322bcf23fa602c690000c04b0395000000200016bb4fbd4e01414d3215800802940ab108fff2030d2000110126200000001aee5103be050a15f6f1ffc8404d8800000006bb97c18e0142857dfa800010146200000001aee89099a050a15f8720000b05dd000000800046be3743b781428d80e1b00002879b00514b4404f63600827d8c09e22c000400015ffe6007016190000402582ce8000004000135ecee1de80a146c02e54758143cd8059ad3e027b1b00613dd004f102c360000804b055d000000200046bcc7c3c781428d80108c6e02829b002b2ece050a16019a4b29b00ab5c3604f136004e410409ec018a10000960c3a00000080004d7de9878602851b003923cc05053601623b440a0a6bfb8c3a5014140b0640005012c197400000100005afe570ef2050a36003a47c80a0a6bfd2c45f014140b054000501101a8200000001b05a90edc050535ffe605800a0a101b8200000001b08a30ec0050535ffe605300a0a101c8200000005b0c6f0ea4050515ffca0568b0001000e"); + data.spatList.add("001338000817a780000089680500204642b342b34802021a15a955a940181190acd0acd20100868555c555c00104342aae2aae002821a155715570"); + + data.ID = UUID.randomUUID().toString().replace("-", ""); + data.uploadTime = Instant.now().toEpochMilli(); + + return data; + } + +} From fb519855b7f85e1d8c8314b251244ff8b8ea72c9 Mon Sep 17 00:00:00 2001 From: John-Wiens Date: Tue, 7 May 2024 16:17:19 -0600 Subject: [PATCH 02/29] Adding Asn.1 Decoder Class --- .../us/dot/its/jpo/ode/api/asn1/Decoder.java | 279 ++++++++++++++++++ .../dot/its/jpo/ode/api/models/Message.java | 13 + .../its/jpo/ode/api/models/MessageType.java | 11 + .../its/jpo/ode/api/models/TypePayload.java | 17 ++ 4 files changed, 320 insertions(+) create mode 100644 api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/Decoder.java create mode 100644 api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/Message.java create mode 100644 api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/MessageType.java create mode 100644 api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/TypePayload.java diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/Decoder.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/Decoder.java new file mode 100644 index 00000000..f49dcd9e --- /dev/null +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/Decoder.java @@ -0,0 +1,279 @@ +package us.dot.its.jpo.ode.api.asn1; + +import lombok.extern.slf4j.Slf4j; +import us.dot.its.jpo.ode.api.models.Message; +import us.dot.its.jpo.ode.api.models.MessageType; +import us.dot.its.jpo.ode.api.models.TypePayload; +import us.dot.its.jpo.ode.coder.OdeBsmDataCreatorHelper; +import us.dot.its.jpo.ode.model.Asn1Encoding; +import us.dot.its.jpo.ode.model.Asn1Encoding.EncodingRule; +import us.dot.its.jpo.ode.model.OdeAsn1Data; +import us.dot.its.jpo.ode.model.OdeAsn1Payload; +import us.dot.its.jpo.ode.model.OdeBsmData; +import us.dot.its.jpo.ode.model.OdeBsmMetadata; +import us.dot.its.jpo.ode.model.OdeBsmMetadata.BsmSource; +import us.dot.its.jpo.ode.model.OdeData; +import us.dot.its.jpo.ode.model.OdeHexByteArray; +import us.dot.its.jpo.ode.model.OdeLogMetadata.RecordType; +import us.dot.its.jpo.ode.model.OdeLogMetadata.SecurityResultCode; +import us.dot.its.jpo.ode.model.OdeLogMsgMetadataLocation; +import us.dot.its.jpo.ode.model.OdeMsgPayload; +import us.dot.its.jpo.ode.model.ReceivedMessageDetails; +import us.dot.its.jpo.ode.model.RxSource; +import us.dot.its.jpo.ode.util.XmlUtils; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.ArrayUtils; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.core.JsonProcessingException; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.UUID; + +// import static us.dot.its.jpo.ode.api.asn1.extractMessageFrame; + + + +@Component +@Slf4j +public class Decoder { + + public static final MessageType[] types = {MessageType.BSM, MessageType.MAP, MessageType.SPAT, MessageType.SRM, MessageType.SSM, MessageType.TIM}; + public static final String[] startFlags = {"0014", "0012", "0013", "001d", "001e", "001f"}; //BSM, MAP, SPAT, SRM, SSM, TIM + public static final int[] maxSizes = {500, 2048, 1000, 500, 500, 500}; + public static final int HEADER_MINIMUM_SIZE = 20; + public static final int bufferSize = 2048; + + public static Message decode(String inputAsn1){ + + // Identify Message Type and Cut off any extra characters + TypePayload payload = identifyAsn1(inputAsn1); + + + // Convert Payload to Pojo and add Metadata + OdeData data = getAsOdeData(payload); + + + + XmlUtils xmlUtils = new XmlUtils(); + try { + // Convert to XML for ASN.1 Decoder + String xml = xmlUtils.toXml(data); + System.out.println("XML" + xml); + + // Send String through ASN.1 Decoder to get Decoded XML Data + String decodedXml = decodeXmlWithAcm(xml); + + if(payload.getType() == MessageType.BSM){ + OdeBsmData bsm = OdeBsmDataCreatorHelper.createOdeBsmData(decodedXml); + Message message = new Message(); + message.setAsn1Text(inputAsn1); + message.setDecodedMessage(bsm); + } + + } catch (JsonProcessingException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + + + + return null; + } + + public static Message decodeAsType(String inputAsn1, MessageType type){ + String payload = removeHeader(inputAsn1, type); + + if(payload != null){ + + } + + return null; + + } + + public static String getOdeReceivedAt(){ + ZonedDateTime utc = ZonedDateTime.now(ZoneOffset.UTC); + String timestamp = utc.format(DateTimeFormatter.ISO_INSTANT); + return timestamp; + } + + public static OdeData getAsOdeData(TypePayload payload){ + + if(payload.getType() == MessageType.BSM){ + return getBsmAsOdeData(payload.getPayload()); + } + + return null; + + } + + public static OdeData getBsmAsOdeData(String encodedPayload){ + OdeMsgPayload payload = new OdeAsn1Payload(new OdeHexByteArray(encodedPayload)); + + // construct metadata + OdeBsmMetadata metadata = new OdeBsmMetadata(payload); + metadata.setOdeReceivedAt(getOdeReceivedAt()); + metadata.setRecordType(RecordType.bsmTx); + metadata.setSecurityResultCode(SecurityResultCode.success); + + // construct metadata: receivedMessageDetails + ReceivedMessageDetails receivedMessageDetails = new ReceivedMessageDetails(); + receivedMessageDetails.setRxSource(RxSource.RV); + + // construct metadata: locationData + OdeLogMsgMetadataLocation locationData = new OdeLogMsgMetadataLocation(); + receivedMessageDetails.setLocationData(locationData); + + metadata.setReceivedMessageDetails(receivedMessageDetails); + metadata.setBsmSource(BsmSource.RV); + + Asn1Encoding unsecuredDataEncoding = new Asn1Encoding("unsecuredData", "MessageFrame", + EncodingRule.UPER); + metadata.addEncoding(unsecuredDataEncoding); + + // construct odeData + return new OdeAsn1Data(metadata, payload); + } + + + + + + + + public static String removeHeader(String hexPacket, MessageType type) { + + // logger.debug("BSM packet length: {}, start index: {}", + // hexPacket.length(), startIndex); + + String startFlag = startFlags[ArrayUtils.indexOf(types, type)]; + + int startIndex = hexPacket.indexOf(startFlag); + if (startIndex == 0) { + // Raw Message no Headers + } else if (startIndex == -1) { + + return null; + } else { + // We likely found a message with a header, look past the first 20 + // bytes for the start of the BSM + int trueStartIndex = HEADER_MINIMUM_SIZE + + hexPacket.substring(HEADER_MINIMUM_SIZE, hexPacket.length()).indexOf(startFlag); + hexPacket = hexPacket.substring(trueStartIndex, hexPacket.length()); + } + + return hexPacket; + } + + + public static TypePayload identifyAsn1(String hexPacket){ + + + // Compute the Effective End Location of the real data. + //int endIndex = hexPacket.indexOf("0000000000000000"); + //if(endIndex == -1){ + int endIndex = hexPacket.length()-1; + //} + + int closestStartIndex = endIndex; + MessageType closestMessageType = MessageType.UNKNOWN; + int closestBufferSize = bufferSize; + + + for(int i = 0; i< startFlags.length; i++){ + + String startFlag = startFlags[i]; + MessageType mType = types[i]; + int typeBufferSize = maxSizes[i]; + + + // Skip possible message type if packet is too big + if(endIndex > typeBufferSize*2){ + continue; + } + + + int startIndex = hexPacket.indexOf(startFlag); + + if (startIndex == 0) { + return new TypePayload(mType, hexPacket); + }else if (startIndex == -1) { + continue; + } else{ + int trueStartIndex = hexPacket.substring(HEADER_MINIMUM_SIZE, hexPacket.length()).indexOf(startFlag); + if(trueStartIndex ==-1){ + continue; + } + trueStartIndex += HEADER_MINIMUM_SIZE; + + while (trueStartIndex != -1 && (trueStartIndex % 2 == 1) && trueStartIndex < hexPacket.length()-4){ + int newStartIndex = hexPacket.substring(trueStartIndex+1, hexPacket.length()).indexOf(startFlag); + if(newStartIndex == -1){ + trueStartIndex = -1; + break; + }else{ + trueStartIndex += newStartIndex+1; + } + } + + if(trueStartIndex != -1 && trueStartIndex < closestStartIndex){ + closestStartIndex = trueStartIndex; + closestMessageType = mType; + closestBufferSize = typeBufferSize; + } + } + } + + if(closestMessageType == MessageType.UNKNOWN){ + return new TypePayload(MessageType.UNKNOWN, hexPacket); + }else{ + return new TypePayload(closestMessageType, hexPacket.substring(closestStartIndex, hexPacket.length())); + } + } + + + + public static String decodeXmlWithAcm(String xmlMessage) throws Exception { + + log.info("Decoding message: {}", xmlMessage); + + // Save XML to temp file + String tempDir = FileUtils.getTempDirectoryPath(); + String tempFileName = "asn1-codec-java-" + UUID.randomUUID().toString() + ".xml"; + log.info("Temp file name: {}", tempFileName); + Path tempFilePath = Path.of(tempDir, tempFileName); + File tempFile = new File(tempFilePath.toString()); + FileUtils.writeStringToFile(tempFile, xmlMessage, StandardCharsets.UTF_8); + + // Run ACM tool to decode message + var pb = new ProcessBuilder( + "/build/acm", "-F", "-c", "/build/config/example.properties", "-T", "decode", tempFile.getAbsolutePath()); + pb.directory(new File("/build")); + Process process = pb.start(); + String result = IOUtils.toString(process.getInputStream(), StandardCharsets.UTF_8); + log.info("Result: {}", result); + + // Clean up temp file + tempFile.delete(); + + return result; + + // // Remove wrapping from result to just return the XER + // try { + // String messageFrame = extractMessageFrame(result); + // log.info("Message frame: {}", messageFrame); + // return messageFrame; + // } catch (Exception e) { + // log.error("Error extracting message frame: {}, returning result which is probably an error message", e); + // return result; + // } + } +} \ No newline at end of file diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/Message.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/Message.java new file mode 100644 index 00000000..5d7fcb76 --- /dev/null +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/Message.java @@ -0,0 +1,13 @@ + + +package us.dot.its.jpo.ode.api.models; + +import lombok.Data; + +@Data +public class Message { + String asn1Text; + // String xmlText; + // String odeJsonText; + T decodedMessage; +} \ No newline at end of file diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/MessageType.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/MessageType.java new file mode 100644 index 00000000..39a0fa7b --- /dev/null +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/MessageType.java @@ -0,0 +1,11 @@ +package us.dot.its.jpo.ode.api.models; + +public enum MessageType { + UNKNOWN, + BSM, + MAP, + SPAT, + SRM, + SSM, + TIM +} diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/TypePayload.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/TypePayload.java new file mode 100644 index 00000000..651bb2eb --- /dev/null +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/TypePayload.java @@ -0,0 +1,17 @@ +package us.dot.its.jpo.ode.api.models; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; + +@ToString +@Setter +@EqualsAndHashCode +@Getter +@AllArgsConstructor +public class TypePayload { + private MessageType type; + private String payload; +} \ No newline at end of file From 7a7f7d4076445c187b8459652c857a8f88ef5be0 Mon Sep 17 00:00:00 2001 From: John-Wiens Date: Wed, 8 May 2024 15:36:07 -0600 Subject: [PATCH 03/29] Assing Basic BSM Message Decoder --- .../jpo/ode/api/ConflictApiApplication.java | 19 +++++- .../us/dot/its/jpo/ode/api/asn1/Decoder.java | 65 +++++++++++++++---- .../dot/its/jpo/ode/api/models/Message.java | 13 ---- .../its/jpo/ode/api/models/UploadAsn1.java | 6 ++ .../models/messages/BsmDecodedMessage.java | 20 ++++++ .../api/models/messages/DecodedMessage.java | 32 +++++++++ 6 files changed, 128 insertions(+), 27 deletions(-) delete mode 100644 api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/Message.java create mode 100644 api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/UploadAsn1.java create mode 100644 api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/BsmDecodedMessage.java create mode 100644 api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/DecodedMessage.java diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/ConflictApiApplication.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/ConflictApiApplication.java index a614cc5f..1e24e263 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/ConflictApiApplication.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/ConflictApiApplication.java @@ -4,6 +4,11 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +import us.dot.its.jpo.ode.api.asn1.Decoder; +import us.dot.its.jpo.ode.api.models.messages.BsmDecodedMessage; +import us.dot.its.jpo.ode.api.models.messages.DecodedMessage; +import us.dot.its.jpo.ode.model.OdeBsmData; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; import org.springframework.scheduling.annotation.EnableScheduling; @@ -22,8 +27,20 @@ public static void main(String[] args) { System.out.println("Started Conflict Monitor API"); System.out.println("Conflict Monitor API docs page found here: http://localhost:8081/swagger-ui/index.html"); System.out.println("Startup Complete"); + + BsmDecodedMessage bsm = (BsmDecodedMessage)Decoder.decode("00145144ad0b7947c2ed9ad2748035a4e8ff880000000fd2229199307d7d07d0b17fff05407d12720038c000fe72c107b001ea88fffeb4002127c0009000000fdfffe3ffff9407344704000041910120100000000efc10609c26e900e11f61a947802127c0009000000fdfffe3ffff9407453304000041910120100000008ffffe501ca508100000000000a508100000404804000000849f00024000003f7fff8ffffe501ca508100000fe501ca508100000fffe501ca51c10000000024000003f7fff8ffffe501ca51c1"); + - + + System.out.println("Fully Decoded BSM" + bsm.getData()); + // OdeBsmData decodeTest; + // try { + // decodeTest = OdeBsmDataCreatorHelper.createOdeBsmData("RVbsmTxsuccessRVunsecuredDataMessageFrameUPERus.dot.its.jpo.ode.model.OdeAsn1Payloadf48cd9fa-cf32-4542-9cfd-4ff0542d56b010002024-05-07T18:45:29.796137Z60false10.165.5.230MessageFrame201052123484129609403192909-111704926414321404081005421320812720012001-12700111132012800-14434616781268-3227713981122522407912025454581096553565535101051"); + // System.out.println(decodeTest); + // } catch (XmlUtilsException e) { + // // TODO Auto-generated catch block + // e.printStackTrace(); + // } } // @Bean diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/Decoder.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/Decoder.java index f49dcd9e..132e6e5b 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/Decoder.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/Decoder.java @@ -1,10 +1,10 @@ package us.dot.its.jpo.ode.api.asn1; import lombok.extern.slf4j.Slf4j; -import us.dot.its.jpo.ode.api.models.Message; import us.dot.its.jpo.ode.api.models.MessageType; import us.dot.its.jpo.ode.api.models.TypePayload; -import us.dot.its.jpo.ode.coder.OdeBsmDataCreatorHelper; +import us.dot.its.jpo.ode.api.models.messages.BsmDecodedMessage; +import us.dot.its.jpo.ode.api.models.messages.DecodedMessage; import us.dot.its.jpo.ode.model.Asn1Encoding; import us.dot.its.jpo.ode.model.Asn1Encoding.EncodingRule; import us.dot.its.jpo.ode.model.OdeAsn1Data; @@ -12,6 +12,7 @@ import us.dot.its.jpo.ode.model.OdeBsmData; import us.dot.its.jpo.ode.model.OdeBsmMetadata; import us.dot.its.jpo.ode.model.OdeBsmMetadata.BsmSource; +import us.dot.its.jpo.ode.model.OdeBsmPayload; import us.dot.its.jpo.ode.model.OdeData; import us.dot.its.jpo.ode.model.OdeHexByteArray; import us.dot.its.jpo.ode.model.OdeLogMetadata.RecordType; @@ -20,13 +21,19 @@ import us.dot.its.jpo.ode.model.OdeMsgPayload; import us.dot.its.jpo.ode.model.ReceivedMessageDetails; import us.dot.its.jpo.ode.model.RxSource; +import us.dot.its.jpo.ode.plugin.j2735.builders.BsmBuilder; +import us.dot.its.jpo.ode.util.JsonUtils; import us.dot.its.jpo.ode.util.XmlUtils; +import us.dot.its.jpo.ode.util.XmlUtils.XmlUtilsException; +import us.dot.its.jpo.ode.context.AppContext; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.ArrayUtils; import org.springframework.stereotype.Component; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import java.io.*; import java.nio.charset.StandardCharsets; @@ -50,7 +57,7 @@ public class Decoder { public static final int HEADER_MINIMUM_SIZE = 20; public static final int bufferSize = 2048; - public static Message decode(String inputAsn1){ + public static DecodedMessage decode(String inputAsn1){ // Identify Message Type and Cut off any extra characters TypePayload payload = identifyAsn1(inputAsn1); @@ -59,22 +66,21 @@ public static Message decode(String inputAsn1){ // Convert Payload to Pojo and add Metadata OdeData data = getAsOdeData(payload); - - XmlUtils xmlUtils = new XmlUtils(); + try { // Convert to XML for ASN.1 Decoder String xml = xmlUtils.toXml(data); System.out.println("XML" + xml); // Send String through ASN.1 Decoder to get Decoded XML Data - String decodedXml = decodeXmlWithAcm(xml); + // String decodedXml = decodeXmlWithAcm(xml); + String decodedXml = mockDecodeXmlWithAcm(xml); - if(payload.getType() == MessageType.BSM){ - OdeBsmData bsm = OdeBsmDataCreatorHelper.createOdeBsmData(decodedXml); - Message message = new Message(); - message.setAsn1Text(inputAsn1); - message.setDecodedMessage(bsm); + if(payload.getType().equals(MessageType.BSM)){ + OdeBsmData bsm = createOdeBsmData(decodedXml); + DecodedMessage message = new BsmDecodedMessage(bsm, inputAsn1, MessageType.BSM, ""); + return message; } } catch (JsonProcessingException e) { @@ -88,7 +94,7 @@ public static Message decode(String inputAsn1){ return null; } - public static Message decodeAsType(String inputAsn1, MessageType type){ + public static DecodedMessage decodeAsType(String inputAsn1, MessageType type){ String payload = removeHeader(inputAsn1, type); if(payload != null){ @@ -143,6 +149,31 @@ public static OdeData getBsmAsOdeData(String encodedPayload){ return new OdeAsn1Data(metadata, payload); } + public static OdeBsmData createOdeBsmData(String consumedData) throws XmlUtilsException { + ObjectNode consumed = XmlUtils.toObjectNode(consumedData); + + JsonNode metadataNode = consumed.findValue(AppContext.METADATA_STRING); + if (metadataNode instanceof ObjectNode) { + ObjectNode object = (ObjectNode) metadataNode; + object.remove(AppContext.ENCODINGS_STRING); + } + + OdeBsmMetadata metadata = (OdeBsmMetadata) JsonUtils.fromJson( + metadataNode.toString(), OdeBsmMetadata.class); + + /* + * ODE-755 and ODE-765 Starting with schemaVersion=5 receivedMessageDetails + * will be present in BSM metadata. None should be present in prior versions. + */ + if (metadata.getSchemaVersion() <= 4) { + metadata.setReceivedMessageDetails(null); + } + + OdeBsmPayload payload = new OdeBsmPayload( + BsmBuilder.genericBsm(consumed.findValue("BasicSafetyMessage"))); + return new OdeBsmData(metadata, payload); + } + @@ -240,15 +271,22 @@ public static TypePayload identifyAsn1(String hexPacket){ } + public static String mockDecodeXmlWithAcm(String xmlMessage){ + return "RVbsmTxsuccessRVunsecuredDataMessageFrameUPERus.dot.its.jpo.ode.model.OdeAsn1Payload8829c539-e684-40b7-a786-692acd3f897a10002024-05-08T20:47:53.830130272Z60falseMessageFrame2018B42DE51F299800000810013156448-100500000002505900-79521090627893276718020065535655351010527101001000000000000000"; + } + public static String decodeXmlWithAcm(String xmlMessage) throws Exception { + + System.out.println("Decoding Message: " + xmlMessage); log.info("Decoding message: {}", xmlMessage); // Save XML to temp file String tempDir = FileUtils.getTempDirectoryPath(); String tempFileName = "asn1-codec-java-" + UUID.randomUUID().toString() + ".xml"; log.info("Temp file name: {}", tempFileName); + System.out.println("Temp File Name: " + tempFileName); Path tempFilePath = Path.of(tempDir, tempFileName); File tempFile = new File(tempFilePath.toString()); FileUtils.writeStringToFile(tempFile, xmlMessage, StandardCharsets.UTF_8); @@ -260,9 +298,10 @@ public static String decodeXmlWithAcm(String xmlMessage) throws Exception { Process process = pb.start(); String result = IOUtils.toString(process.getInputStream(), StandardCharsets.UTF_8); log.info("Result: {}", result); + System.out.println("Decode Result: " + result); // Clean up temp file - tempFile.delete(); + // tempFile.delete(); return result; diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/Message.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/Message.java deleted file mode 100644 index 5d7fcb76..00000000 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/Message.java +++ /dev/null @@ -1,13 +0,0 @@ - - -package us.dot.its.jpo.ode.api.models; - -import lombok.Data; - -@Data -public class Message { - String asn1Text; - // String xmlText; - // String odeJsonText; - T decodedMessage; -} \ No newline at end of file diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/UploadAsn1.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/UploadAsn1.java new file mode 100644 index 00000000..06e7c048 --- /dev/null +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/UploadAsn1.java @@ -0,0 +1,6 @@ +package us.dot.its.jpo.ode.api.models; + +public class UploadAsn1 { + MessageType type; + String asn1; +} diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/BsmDecodedMessage.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/BsmDecodedMessage.java new file mode 100644 index 00000000..1bb2ed5a --- /dev/null +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/BsmDecodedMessage.java @@ -0,0 +1,20 @@ +package us.dot.its.jpo.ode.api.models.messages; + +import us.dot.its.jpo.ode.api.models.MessageType; +import us.dot.its.jpo.ode.model.OdeBsmData; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@ToString +@Setter +@Getter +public class BsmDecodedMessage extends DecodedMessage{ + public OdeBsmData data; + + public BsmDecodedMessage(OdeBsmData data, String asn1Text, MessageType type, String decodeErrors) { + super(asn1Text, type, decodeErrors); + this.data = data; + } + +} diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/DecodedMessage.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/DecodedMessage.java new file mode 100644 index 00000000..4a2c5ff6 --- /dev/null +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/DecodedMessage.java @@ -0,0 +1,32 @@ + + +package us.dot.its.jpo.ode.api.models.messages; + +import java.time.Instant; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import us.dot.its.jpo.ode.api.models.MessageType; + +@ToString +@Setter +@EqualsAndHashCode +@Getter +public class DecodedMessage { + String asn1Text; + long decodeTime; + String decodeErrors; + String type; + + public DecodedMessage(String asn1Text, MessageType type, String decodeErrors){ + this.asn1Text = asn1Text; + this.decodeTime = Instant.now().toEpochMilli(); + this.decodeErrors = decodeErrors; + this.type = type.name(); + } +} + + + From 6a81a48c3eaf7018fe67fcb6ceb06f0059273717 Mon Sep 17 00:00:00 2001 From: John-Wiens Date: Wed, 8 May 2024 15:39:00 -0600 Subject: [PATCH 04/29] Adding asn1_codec dependency --- api/asn1_codec | 1 + 1 file changed, 1 insertion(+) create mode 160000 api/asn1_codec diff --git a/api/asn1_codec b/api/asn1_codec new file mode 160000 index 00000000..422bcab4 --- /dev/null +++ b/api/asn1_codec @@ -0,0 +1 @@ +Subproject commit 422bcab41e119dafa39eb1ca812b68e06ca4dbbe From d6c4c240db4b22d722adcfb9433d01204bfb4819 Mon Sep 17 00:00:00 2001 From: John-Wiens Date: Wed, 8 May 2024 15:39:48 -0600 Subject: [PATCH 05/29] Updating Dockerfile to build asn1c_decoder --- api/Dockerfile | 227 +++++++++++++++++++++++++++++++++++++++++++-- docker-compose.yml | 5 + 2 files changed, 222 insertions(+), 10 deletions(-) diff --git a/api/Dockerfile b/api/Dockerfile index 55f1be47..b4fecab6 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -1,4 +1,125 @@ -FROM maven:3.8-eclipse-temurin-21-alpine as builder +# FROM maven:3.8-eclipse-temurin-21-alpine as builder + +# WORKDIR /home + +# # Copy only the files needed to avoid putting all sorts of junk from your local env on to the image +# COPY ./jpo-conflictmonitor/jpo-geojsonconverter/jpo-ode/pom.xml ./jpo-ode/ +# COPY ./jpo-conflictmonitor/jpo-geojsonconverter/jpo-ode/jpo-ode-common/pom.xml ./jpo-ode/jpo-ode-common/ +# COPY ./jpo-conflictmonitor/jpo-geojsonconverter/jpo-ode/jpo-ode-common/src ./jpo-ode/jpo-ode-common/src +# COPY ./jpo-conflictmonitor/jpo-geojsonconverter/jpo-ode/jpo-ode-plugins/pom.xml ./jpo-ode/jpo-ode-plugins/ +# COPY ./jpo-conflictmonitor/jpo-geojsonconverter/jpo-ode/jpo-ode-plugins/src ./jpo-ode/jpo-ode-plugins/src +# COPY ./jpo-conflictmonitor/jpo-geojsonconverter/jpo-ode/jpo-ode-core/pom.xml ./jpo-ode/jpo-ode-core/ +# COPY ./jpo-conflictmonitor/jpo-geojsonconverter/jpo-ode/jpo-ode-core/src ./jpo-ode/jpo-ode-core/src/ +# COPY ./jpo-conflictmonitor/jpo-geojsonconverter/jpo-ode/jpo-ode-svcs/pom.xml ./jpo-ode/jpo-ode-svcs/ +# COPY ./jpo-conflictmonitor/jpo-geojsonconverter/jpo-ode/jpo-ode-svcs/src ./jpo-ode/jpo-ode-svcs/src + +# COPY ./jpo-conflictmonitor/jpo-geojsonconverter/jpo-geojsonconverter/pom.xml ./jpo-geojsonconverter/ +# COPY ./jpo-conflictmonitor/jpo-geojsonconverter/jpo-geojsonconverter/src ./jpo-geojsonconverter/src + +# COPY ./jpo-conflictmonitor/jpo-conflictmonitor/pom.xml ./jpo-conflictmonitor/ +# COPY ./jpo-conflictmonitor/jpo-conflictmonitor/src ./jpo-conflictmonitor/src + +# COPY ./jpo-conflictvisualizer-api/pom.xml ./jpo-conflictvisualizer-api/ +# COPY ./jpo-conflictvisualizer-api/src ./jpo-conflictvisualizer-api/src + +# WORKDIR /home/jpo-ode + +# RUN mvn install -DskipTests + +# WORKDIR /home/jpo-geojsonconverter + +# RUN mvn clean install -DskipTests + +# WORKDIR /home/jpo-conflictmonitor + +# RUN mvn clean install -DskipTests + +# WORKDIR /home/jpo-conflictvisualizer-api + +# RUN mvn clean package -DskipTests +# # ENTRYPOINT ["tail", "-f", "/dev/null"] +# FROM eclipse-temurin:21-jre-alpine + +# WORKDIR /home + +# COPY --from=builder /home/jpo-conflictvisualizer-api/src/main/resources/application.yaml /home +# COPY --from=builder /home/jpo-conflictvisualizer-api/src/main/resources/logback.xml /home +# COPY --from=builder /home/jpo-conflictvisualizer-api/target/jpo-conflictvisualizer-api-0.0.1-SNAPSHOT.jar /home + +# #COPY cert.crt /home/cert.crt +# #RUN keytool -import -trustcacerts -keystore /opt/java/openjdk/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-conflictvisualizer-api-0.0.1-SNAPSHOT.jar"] + +# # ENTRYPOINT ["tail", "-f", "/dev/null"] + + +# === BUILDER IMAGE for ACM === +FROM alpine:3.12 as builder +USER root +WORKDIR /asn1_codec + +# add build dependencies +RUN apk add --upgrade --no-cache --virtual .build-deps \ + cmake \ + g++ \ + make \ + bash \ + automake \ + libtool \ + autoconf \ + librdkafka \ + librdkafka-dev \ + flex \ + bison + +# Install pugixml +ADD ./asn1_codec/pugixml /asn1_codec/pugixml +RUN cd /asn1_codec/pugixml && mkdir -p build && cd build && cmake .. && make && make install + +# Build and install asn1c submodule +ADD ./asn1_codec/asn1c /asn1_codec/asn1c +RUN cd asn1c && test -f configure || autoreconf -iv && ./configure && make && make install + +# Make generated files available to the build & compile example +RUN export LD_LIBRARY_PATH=/usr/local/lib +ADD ./asn1_codec/asn1c_combined /asn1_codec/asn1c_combined +RUN cd /asn1_codec/asn1c_combined && bash doIt.sh + +# Remove any lingering .asn files +RUN rm -rf /asn1c_codec/asn1c_combined/j2735-asn-files +RUN rm -rf /asn1c_codec/asn1c_combined/semi-asn-files + +# Remove duplicate files +RUN rm -rf /asn1c_codec/asn1c_combined/generated-files + +# add the source and build files +ADD ./asn1_codec/CMakeLists.txt /asn1_codec +ADD ./asn1_codec/config /asn1_codec/config +ADD ./asn1_codec/include /asn1_codec/include +ADD ./asn1_codec/src /asn1_codec/src +ADD ./asn1_codec/kafka-test /asn1_codec/kafka-test +ADD ./asn1_codec/unit-test-data /asn1_codec/unit-test-data +ADD ./asn1_codec/data /asn1_codec/data +ADD ./asn1_codec/run_acm.sh /asn1_codec +ADD ./asn1_codec/data /asn1_codec/data + +# Build acm. +RUN mkdir -p /build && cd /build && cmake /asn1_codec && make + + +# === Build image for Java === +FROM maven:3.8-eclipse-temurin-21-alpine as jbuilder WORKDIR /home @@ -38,16 +159,104 @@ WORKDIR /home/jpo-conflictvisualizer-api RUN mvn clean package -DskipTests # ENTRYPOINT ["tail", "-f", "/dev/null"] -FROM eclipse-temurin:21-jre-alpine + + +# === RUNTIME IMAGE for Java and ACM === +# Using Alpine 3.12 because ACM does not compile or run on the latest Alpine (3.19) +FROM alpine:3.12 WORKDIR /home -COPY --from=builder /home/jpo-conflictvisualizer-api/src/main/resources/application.yaml /home -COPY --from=builder /home/jpo-conflictvisualizer-api/src/main/resources/logback.xml /home -COPY --from=builder /home/jpo-conflictvisualizer-api/target/jpo-conflictvisualizer-api-0.0.1-SNAPSHOT.jar /home +# Copy java executable +COPY --from=jbuilder /home/jpo-conflictvisualizer-api/src/main/resources/application.yaml /home +COPY --from=jbuilder /home/jpo-conflictvisualizer-api/src/main/resources/logback.xml /home +COPY --from=jbuilder /home/jpo-conflictvisualizer-api/target/jpo-conflictvisualizer-api-0.0.1-SNAPSHOT.jar /home + +# Copy asn1_codec executable and test files +USER root +WORKDIR /asn1_codec + +# add runtime dependencies +RUN apk add --upgrade --no-cache \ + bash \ + librdkafka \ + librdkafka-dev + +# copy the built files from the builder +COPY --from=builder /asn1_codec /asn1_codec +COPY --from=builder /build /build + +# Add test data. This changes frequently so keep it low in the file. +ADD ./asn1_codec/docker-test /asn1_codec/docker-test + +# Put workdir back to Java home +WORKDIR /home -#COPY cert.crt /home/cert.crt -#RUN keytool -import -trustcacerts -keystore /opt/java/openjdk/lib/security/cacerts -storepass changeit -noprompt -alias mycert -file cert.crt + +# Install Java 21, not available from apk in Alpine 3.12 +# Dockerfile for eclipse-temurin:21-jre-alpine copied from: +# https://github.com/adoptium/containers/blob/057e5aa7581e96b8a2334290e750b329d62abfdf/21/jre/alpine/Dockerfile + +ENV JAVA_HOME /opt/java/openjdk +ENV PATH $JAVA_HOME/bin:$PATH + +# Default to UTF-8 file.encoding +ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' LC_ALL='en_US.UTF-8' + +RUN set -eux; \ + apk add --no-cache \ + # java.lang.UnsatisfiedLinkError: libfontmanager.so: libfreetype.so.6: cannot open shared object file: No such file or directory + # java.lang.NoClassDefFoundError: Could not initialize class sun.awt.X11FontManager + # https://github.com/docker-library/openjdk/pull/235#issuecomment-424466077 + fontconfig ttf-dejavu \ + # utilities for keeping Alpine and OpenJDK CA certificates in sync + # https://github.com/adoptium/containers/issues/293 + ca-certificates p11-kit-trust \ + # locales ensures proper character encoding and locale-specific behaviors using en_US.UTF-8 + musl-locales musl-locales-lang \ + tzdata \ + ; \ + rm -rf /var/cache/apk/* + +ENV JAVA_VERSION jdk-21.0.3+9 + +RUN set -eux; \ + ARCH="$(apk --print-arch)"; \ + case "${ARCH}" in \ + aarch64|arm64) \ + ESUM='54e8618da373258654fe788d509f087d3612de9e080eb6831601069dbc8a4b2b'; \ + BINARY_URL='https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.3%2B9/OpenJDK21U-jre_aarch64_alpine-linux_hotspot_21.0.3_9.tar.gz'; \ + ;; \ + amd64|x86_64) \ + ESUM='b3e7170deab11a7089fe8e14f9f398424fd86db085f745dad212f6cfc4121df6'; \ + BINARY_URL='https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.3%2B9/OpenJDK21U-jre_x64_alpine-linux_hotspot_21.0.3_9.tar.gz'; \ + ;; \ + *) \ + echo "Unsupported arch: ${ARCH}"; \ + exit 1; \ + ;; \ + esac; \ + wget -O /tmp/openjdk.tar.gz ${BINARY_URL}; \ + echo "${ESUM} */tmp/openjdk.tar.gz" | sha256sum -c -; \ + mkdir -p "$JAVA_HOME"; \ + tar --extract \ + --file /tmp/openjdk.tar.gz \ + --directory "$JAVA_HOME" \ + --strip-components 1 \ + --no-same-owner \ + ; \ + rm -f /tmp/openjdk.tar.gz ${JAVA_HOME}/lib/src.zip; + +RUN set -eux; \ + echo "Verifying install ..."; \ + echo "java --version"; java --version; \ + echo "Complete." + + + +# ENTRYPOINT ["java", "-jar", "/home/asn1-codec-java.jar"] +# #COPY cert.crt /home/cert.crt +# #RUN keytool -import -trustcacerts -keystore /opt/java/openjdk/lib/security/cacerts -storepass changeit -noprompt -alias mycert -file cert.crt ENTRYPOINT ["java", \ "-Djava.rmi.server.hostname=$DOCKER_HOST_IP", \ @@ -59,6 +268,4 @@ ENTRYPOINT ["java", \ "-Dcom.sun.management.jmxremote.ssl=false", \ "-Dlogback.configurationFile=/home/logback.xml", \ "-jar", \ - "/home/jpo-conflictvisualizer-api-0.0.1-SNAPSHOT.jar"] - -# ENTRYPOINT ["tail", "-f", "/dev/null"] + "/home/jpo-conflictvisualizer-api-0.0.1-SNAPSHOT.jar"] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 73ba9f7e..5f7ffa1a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -65,6 +65,11 @@ services: SPRING_KAFKA_BOOTSTRAPSERVERS: ${KAFKA_BROKER_IP}:${KAFKA_BROKER_PORT} CM_SERVER_URL: ${CM_SERVER_URL} load: "false" + KAFKA_TYPE: "ON-PREM" + ACM_CONFIG_FILE: adm.properties + ACM_LOG_TO_CONSOLE: true + ACM_LOG_TO_FILE: false + ACM_LOG_LEVEL: DEBUG #KAFKA_BROKER_IP: ${KAFKA_BROKER_IP} #KAFKA_BROKER_PORT: ${KAFKA_BROKER_PORT} logging: From 23d8103a0aea10075d79b428e516b5f73f3cb148 Mon Sep 17 00:00:00 2001 From: John-Wiens Date: Wed, 8 May 2024 15:51:59 -0600 Subject: [PATCH 06/29] Added classes for decoded message types. --- .../models/messages/BsmDecodedMessage.java | 6 ++--- .../models/messages/MapDecodedMessage.java | 25 +++++++++++++++++++ .../models/messages/SpatDecodedMessage.java | 24 ++++++++++++++++++ .../models/messages/SrmDecodedMessage.java | 20 +++++++++++++++ .../models/messages/SsmDecodedMessage.java | 20 +++++++++++++++ .../models/messages/TimDecodedMessage.java | 20 +++++++++++++++ 6 files changed, 112 insertions(+), 3 deletions(-) create mode 100644 api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/MapDecodedMessage.java create mode 100644 api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SpatDecodedMessage.java create mode 100644 api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SrmDecodedMessage.java create mode 100644 api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SsmDecodedMessage.java create mode 100644 api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/TimDecodedMessage.java diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/BsmDecodedMessage.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/BsmDecodedMessage.java index 1bb2ed5a..276ea0ba 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/BsmDecodedMessage.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/BsmDecodedMessage.java @@ -10,11 +10,11 @@ @Setter @Getter public class BsmDecodedMessage extends DecodedMessage{ - public OdeBsmData data; + public OdeBsmData bsm; - public BsmDecodedMessage(OdeBsmData data, String asn1Text, MessageType type, String decodeErrors) { + public BsmDecodedMessage(OdeBsmData bsm, String asn1Text, MessageType type, String decodeErrors) { super(asn1Text, type, decodeErrors); - this.data = data; + this.bsm = bsm; } } diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/MapDecodedMessage.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/MapDecodedMessage.java new file mode 100644 index 00000000..91608b9f --- /dev/null +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/MapDecodedMessage.java @@ -0,0 +1,25 @@ +package us.dot.its.jpo.ode.api.models.messages; + + +import us.dot.its.jpo.geojsonconverter.pojos.geojson.LineString; +import us.dot.its.jpo.geojsonconverter.pojos.geojson.map.ProcessedMap; +import us.dot.its.jpo.ode.api.models.MessageType; +import us.dot.its.jpo.ode.model.OdeMapData; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@ToString +@Setter +@Getter +public class MapDecodedMessage extends DecodedMessage{ + public OdeMapData map; + public ProcessedMap processedMap; + + public MapDecodedMessage(ProcessedMap processedMap, OdeMapData map, String asn1Text, MessageType type, String decodeErrors) { + super(asn1Text, type, decodeErrors); + this.processedMap = processedMap; + this.map = map; + } + +} diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SpatDecodedMessage.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SpatDecodedMessage.java new file mode 100644 index 00000000..375c96eb --- /dev/null +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SpatDecodedMessage.java @@ -0,0 +1,24 @@ +package us.dot.its.jpo.ode.api.models.messages; + +import us.dot.its.jpo.geojsonconverter.pojos.spat.ProcessedSpat; +import us.dot.its.jpo.ode.api.models.MessageType; +import us.dot.its.jpo.ode.model.OdeSpatData; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@ToString +@Setter +@Getter +public class SpatDecodedMessage extends DecodedMessage{ + public OdeSpatData spat; + public ProcessedSpat processedSpat; + + public SpatDecodedMessage(ProcessedSpat processedSpat, OdeSpatData spat, String asn1Text, MessageType type, String decodeErrors) { + super(asn1Text, type, decodeErrors); + this.processedSpat = processedSpat; + this.spat = spat; + + } + +} diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SrmDecodedMessage.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SrmDecodedMessage.java new file mode 100644 index 00000000..2281c628 --- /dev/null +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SrmDecodedMessage.java @@ -0,0 +1,20 @@ +package us.dot.its.jpo.ode.api.models.messages; + +import us.dot.its.jpo.ode.api.models.MessageType; +import us.dot.its.jpo.ode.model.OdeSrmData; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@ToString +@Setter +@Getter +public class SrmDecodedMessage extends DecodedMessage{ + public OdeSrmData srm; + + public SrmDecodedMessage(OdeSrmData srm, String asn1Text, MessageType type, String decodeErrors) { + super(asn1Text, type, decodeErrors); + this.srm = srm; + } + +} diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SsmDecodedMessage.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SsmDecodedMessage.java new file mode 100644 index 00000000..82759dab --- /dev/null +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SsmDecodedMessage.java @@ -0,0 +1,20 @@ +package us.dot.its.jpo.ode.api.models.messages; + +import us.dot.its.jpo.ode.api.models.MessageType; +import us.dot.its.jpo.ode.model.OdeSsmData; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@ToString +@Setter +@Getter +public class SsmDecodedMessage extends DecodedMessage{ + public OdeSsmData ssm; + + public SsmDecodedMessage(OdeSsmData ssm, String asn1Text, MessageType type, String decodeErrors) { + super(asn1Text, type, decodeErrors); + this.ssm = ssm; + } + +} diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/TimDecodedMessage.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/TimDecodedMessage.java new file mode 100644 index 00000000..f57d85b0 --- /dev/null +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/TimDecodedMessage.java @@ -0,0 +1,20 @@ +package us.dot.its.jpo.ode.api.models.messages; + +import us.dot.its.jpo.ode.api.models.MessageType; +import us.dot.its.jpo.ode.model.OdeTimData; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@ToString +@Setter +@Getter +public class TimDecodedMessage extends DecodedMessage{ + public OdeTimData tim; + + public TimDecodedMessage(OdeTimData tim, String asn1Text, MessageType type, String decodeErrors) { + super(asn1Text, type, decodeErrors); + this.tim = tim; + } + +} From fc2103f436b982fb5a559044688a020cc46e5916 Mon Sep 17 00:00:00 2001 From: John-Wiens Date: Wed, 8 May 2024 16:20:36 -0600 Subject: [PATCH 07/29] Updating Bsm Reference --- .../java/us/dot/its/jpo/ode/api/ConflictApiApplication.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/ConflictApiApplication.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/ConflictApiApplication.java index 1e24e263..530b8189 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/ConflictApiApplication.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/ConflictApiApplication.java @@ -32,7 +32,7 @@ public static void main(String[] args) { - System.out.println("Fully Decoded BSM" + bsm.getData()); + System.out.println("Fully Decoded BSM" + bsm.getBsm()); // OdeBsmData decodeTest; // try { // decodeTest = OdeBsmDataCreatorHelper.createOdeBsmData("RVbsmTxsuccessRVunsecuredDataMessageFrameUPERus.dot.its.jpo.ode.model.OdeAsn1Payloadf48cd9fa-cf32-4542-9cfd-4ff0542d56b010002024-05-07T18:45:29.796137Z60false10.165.5.230MessageFrame201052123484129609403192909-111704926414321404081005421320812720012001-12700111132012800-14434616781268-3227713981122522407912025454581096553565535101051"); From f802cf70997c66b8b5ba687bdb054eb8b6be590a Mon Sep 17 00:00:00 2001 From: John-Wiens Date: Thu, 9 May 2024 12:44:23 -0600 Subject: [PATCH 08/29] Created Mock Decoder Data Sources --- .../accessors/map/ProcessedMapRepository.java | 2 +- .../map/ProcessedMapRepositoryImpl.java | 9 ++- .../api/controllers/DecoderController.java | 68 +++++++++++++++---- .../ode/api/controllers/MapController.java | 4 +- .../ode/api/controllers/StompController.java | 4 -- .../its/jpo/ode/api/models/UploadData.java | 24 ------- .../api/models/messages/EncodedMessage.java | 30 ++++++++ .../mockdata/MockDecodedMessageGenerator.java | 43 ++++++++++++ .../jpo/ode/mockdata/MockMapGenerator.java | 34 ++++++++-- .../jpo/ode/mockdata/MockSpatGenerator.java | 25 ++++++- .../jpo/ode/mockdata/MockSrmGenerator.java | 39 +++++++++++ .../jpo/ode/mockdata/MockSsmGenerator.java | 39 +++++++++++ .../jpo/ode/mockdata/MockTimGenerator.java | 39 +++++++++++ .../ode/mockdata/MockUploadDataGenerator.java | 25 ------- .../java/us/dot/its/jpo/ode/api/MapTest.java | 5 +- .../map/ProcessedMapRepositoryImplTest.java | 5 +- 16 files changed, 315 insertions(+), 80 deletions(-) delete mode 100644 api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/UploadData.java create mode 100644 api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/EncodedMessage.java create mode 100644 api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockDecodedMessageGenerator.java create mode 100644 api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockSrmGenerator.java create mode 100644 api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockSsmGenerator.java create mode 100644 api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockTimGenerator.java delete mode 100644 api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockUploadDataGenerator.java diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/accessors/map/ProcessedMapRepository.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/accessors/map/ProcessedMapRepository.java index 46c3ce8c..fbe69fe0 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/accessors/map/ProcessedMapRepository.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/accessors/map/ProcessedMapRepository.java @@ -15,7 +15,7 @@ public interface ProcessedMapRepository extends DataLoader findProcessedMaps(Query query); + List> findProcessedMaps(Query query); List getIntersectionIDs(); diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/accessors/map/ProcessedMapRepositoryImpl.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/accessors/map/ProcessedMapRepositoryImpl.java index 23c78be3..abbf2907 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/accessors/map/ProcessedMapRepositoryImpl.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/accessors/map/ProcessedMapRepositoryImpl.java @@ -23,6 +23,7 @@ import org.springframework.data.mongodb.core.query.Query; import org.springframework.stereotype.Component; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.mongodb.MongoException; import com.mongodb.client.DistinctIterable; @@ -50,6 +51,8 @@ public class ProcessedMapRepositoryImpl implements ProcessedMapRepository { @Autowired ConflictMonitorApiProperties props; + TypeReference> processedMapTypeReference = new TypeReference<>(){}; + private String collectionName = "ProcessedMap"; private ObjectMapper mapper = DateJsonMapper.getInstance(); private Logger logger = LoggerFactory.getLogger(ProcessedMapRepositoryImpl.class); @@ -87,12 +90,12 @@ public long getQueryResultCount(Query query) { return mongoTemplate.count(query, ProcessedMap.class, collectionName); } - public List findProcessedMaps(Query query) { + public List> findProcessedMaps(Query query) { List documents = mongoTemplate.find(query, Map.class, collectionName); - List convertedList = new ArrayList<>(); + List> convertedList = new ArrayList<>(); for (Map document : documents) { document.remove("_id"); - ProcessedMap bsm = mapper.convertValue(document, ProcessedMap.class); + ProcessedMap bsm = mapper.convertValue(document, processedMapTypeReference); convertedList.add(bsm); } return convertedList; diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/controllers/DecoderController.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/controllers/DecoderController.java index 10870d6c..f00a83d9 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/controllers/DecoderController.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/controllers/DecoderController.java @@ -15,10 +15,10 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import com.fasterxml.jackson.databind.ObjectMapper; - -import us.dot.its.jpo.ode.api.models.UploadData; -import us.dot.its.jpo.ode.mockdata.MockUploadDataGenerator; +import us.dot.its.jpo.ode.api.models.MessageType; +import us.dot.its.jpo.ode.api.models.messages.EncodedMessage; +import us.dot.its.jpo.ode.mockdata.MockDecodedMessageGenerator; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -45,25 +45,69 @@ public String getCurrentTime() { return ZonedDateTime.now().toInstant().toEpochMilli() + ""; } + // @CrossOrigin(origins = "http://localhost:3000") + // @RequestMapping(value = "/decoder/upload", method = RequestMethod.POST, produces = "application/json") + // public @ResponseBody ResponseEntity new_bulk_upload_request( + // @RequestBody UploadData newUploadData, + // @RequestParam(name = "test", required = false, defaultValue = "false") boolean testData) { + // try { + // logger.info("Uploading Bulk Data"); + + // if (testData) { + // newUploadData = MockUploadDataGenerator.getUploadData(); + // } else { + // return ResponseEntity.status(HttpStatus.NOT_IMPLEMENTED).contentType(MediaType.TEXT_PLAIN) + // .body(newUploadData.toString()); + // } + + // return ResponseEntity.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON) + // .body(newUploadData.toString()); + // } catch (Exception e) { + // logger.info("Failed to Upload Bulk Data"); + // return ResponseEntity.status(HttpStatus.BAD_REQUEST).contentType(MediaType.TEXT_PLAIN) + // .body(ExceptionUtils.getStackTrace(e)); + // } + // } + @CrossOrigin(origins = "http://localhost:3000") @RequestMapping(value = "/decoder/upload", method = RequestMethod.POST, produces = "application/json") - public @ResponseBody ResponseEntity new_bulk_upload_request( - @RequestBody UploadData newUploadData, + public @ResponseBody ResponseEntity decode_request( + @RequestBody EncodedMessage encodedMessage, @RequestParam(name = "test", required = false, defaultValue = "false") boolean testData) { try { - logger.info("Uploading Bulk Data"); - if (testData) { - newUploadData = MockUploadDataGenerator.getUploadData(); + if(encodedMessage.getType() == MessageType.BSM || encodedMessage.getType() == MessageType.UNKNOWN){ + return ResponseEntity.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON) + .body(MockDecodedMessageGenerator.getBsmDecodedMessage().toString()); + } + else if(encodedMessage.getType() == MessageType.MAP){ + return ResponseEntity.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON) + .body(MockDecodedMessageGenerator.getMapDecodedMessage().toString()); + }else if(encodedMessage.getType() == MessageType.SPAT){ + return ResponseEntity.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON) + .body(MockDecodedMessageGenerator.getSpatDecodedMessage().toString()); + }else if(encodedMessage.getType() == MessageType.SRM){ + return ResponseEntity.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON) + .body(MockDecodedMessageGenerator.getSpatDecodedMessage().toString()); + }else if(encodedMessage.getType() == MessageType.SSM){ + return ResponseEntity.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON) + .body(MockDecodedMessageGenerator.getSpatDecodedMessage().toString()); + }else if(encodedMessage.getType() == MessageType.TIM){ + return ResponseEntity.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON) + .body(MockDecodedMessageGenerator.getSpatDecodedMessage().toString()); + }else{ + return ResponseEntity.status(HttpStatus.BAD_REQUEST).contentType(MediaType.TEXT_PLAIN) + .body("No available mapping for Message Type " + encodedMessage.getType()); + } } else { return ResponseEntity.status(HttpStatus.NOT_IMPLEMENTED).contentType(MediaType.TEXT_PLAIN) - .body(newUploadData.toString()); + .body("Not Implemented Yet"); } - return ResponseEntity.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON) - .body(newUploadData.toString()); + // return ResponseEntity.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON) + // .body(newUploadData.toString()); } catch (Exception e) { - logger.info("Failed to Upload Bulk Data"); + logger.info("Failed to Upload Data"); return ResponseEntity.status(HttpStatus.BAD_REQUEST).contentType(MediaType.TEXT_PLAIN) .body(ExceptionUtils.getStackTrace(e)); } diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/controllers/MapController.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/controllers/MapController.java index 47b94beb..0d70c77b 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/controllers/MapController.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/controllers/MapController.java @@ -15,6 +15,8 @@ import org.springframework.web.bind.annotation.RestController; import com.fasterxml.jackson.databind.ObjectMapper; + +import us.dot.its.jpo.geojsonconverter.pojos.geojson.LineString; import us.dot.its.jpo.geojsonconverter.pojos.geojson.map.ProcessedMap; import us.dot.its.jpo.ode.api.ConflictMonitorApiProperties; import us.dot.its.jpo.ode.api.accessors.map.ProcessedMapRepository; @@ -40,7 +42,7 @@ public String getCurrentTime() { @CrossOrigin(origins = "http://localhost:3000") @RequestMapping(value = "/map/json", method = RequestMethod.GET, produces = "application/json") @PreAuthorize("hasRole('USER') || hasRole('ADMIN')") - public ResponseEntity> findMaps( + public ResponseEntity>> findMaps( @RequestParam(name = "intersection_id", required = false) Integer intersectionID, @RequestParam(name = "start_time_utc_millis", required = false) Long startTime, @RequestParam(name = "end_time_utc_millis", required = false) Long endTime, diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/controllers/StompController.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/controllers/StompController.java index 18a5c284..71f9c7f8 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/controllers/StompController.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/controllers/StompController.java @@ -1,17 +1,13 @@ package us.dot.its.jpo.ode.api.controllers; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.messaging.handler.annotation.MessageMapping; -import org.springframework.messaging.handler.annotation.SendTo; import org.springframework.messaging.simp.SimpMessagingTemplate; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Controller; import us.dot.its.jpo.conflictmonitor.monitor.models.bsm.BsmIntersectionIdKey; 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.pojos.spat.ProcessedSpat; -import us.dot.its.jpo.ode.api.models.LiveFeedSessionIndex; import us.dot.its.jpo.ode.model.OdeBsmData; @Controller diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/UploadData.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/UploadData.java deleted file mode 100644 index 6a77ca54..00000000 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/UploadData.java +++ /dev/null @@ -1,24 +0,0 @@ -package us.dot.its.jpo.ode.api.models; - -import lombok.Getter; -import lombok.Setter; -import lombok.ToString; - -import java.util.ArrayList; -import java.util.List; - -import lombok.EqualsAndHashCode; - -@ToString -@Setter -@EqualsAndHashCode -@Getter -public class UploadData { - public boolean test; - public List bsmList = new ArrayList<>(); - public List spatList = new ArrayList<>(); - public List mapList = new ArrayList<>(); - public List genericUpload = new ArrayList<>(); - public Long uploadTime; - public String ID; -} \ No newline at end of file diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/EncodedMessage.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/EncodedMessage.java new file mode 100644 index 00000000..90eeb9da --- /dev/null +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/EncodedMessage.java @@ -0,0 +1,30 @@ +package us.dot.its.jpo.ode.api.models.messages; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import us.dot.its.jpo.ode.api.models.MessageType; + + +@ToString +@Setter +@EqualsAndHashCode +@Getter +public class EncodedMessage { + String asn1Message; + MessageType type; + + public EncodedMessage(String asn1Message, MessageType type){ + this.asn1Message = asn1Message; + this.type = type; + } + + public EncodedMessage(String asn1Message, String type){ + this.asn1Message = asn1Message; + this.type = MessageType.valueOf(type); + } +} + + + diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockDecodedMessageGenerator.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockDecodedMessageGenerator.java new file mode 100644 index 00000000..92c6be7b --- /dev/null +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockDecodedMessageGenerator.java @@ -0,0 +1,43 @@ +package us.dot.its.jpo.ode.mockdata; + +import us.dot.its.jpo.ode.api.models.MessageType; +import us.dot.its.jpo.ode.api.models.messages.BsmDecodedMessage; +import us.dot.its.jpo.ode.api.models.messages.MapDecodedMessage; +import us.dot.its.jpo.ode.api.models.messages.SpatDecodedMessage; +import us.dot.its.jpo.ode.api.models.messages.SrmDecodedMessage; +import us.dot.its.jpo.ode.api.models.messages.SsmDecodedMessage; +import us.dot.its.jpo.ode.api.models.messages.TimDecodedMessage; + +public class MockDecodedMessageGenerator { + + + public static BsmDecodedMessage getBsmDecodedMessage(){ + BsmDecodedMessage message = new BsmDecodedMessage(MockBsmGenerator.getJsonBsms().get(0),"00145144ad0b7947c2ed9ad2748035a4e8ff880000000fd2229199307d7d07d0b17fff05407d12720038c000fe72c107b001ea88fffeb4002127c0009000000fdfffe3ffff9407344704000041910120100000000efc10609c26e900e11f61a947802127c0009000000fdfffe3ffff9407453304000041910120100000008ffffe501ca508100000000000a508100000404804000000849f00024000003f7fff8ffffe501ca508100000fe501ca508100000fffe501ca51c10000000024000003f7fff8ffffe501ca51c1" , MessageType.BSM, ""); + return message; + } + + public static MapDecodedMessage getMapDecodedMessage(){ + MapDecodedMessage message = new MapDecodedMessage(MockMapGenerator.getProcessedMaps().get(0),MockMapGenerator.getJsonMaps().get(0),"0012839338023000205e96094d40df4c2ca626c8516e02dc3c2010640000000289e01c009f603f42e88039900000000a41107b027d80fd0a4200c6400000002973021c09f603de0c16029200000080002a8a008d027d98fee805404fb0e1085f60588200028096021200000080002aa0007d027d98fe9802e04fb1200c214456228000a02b1240005022c03240000020000d56b40bc04fb35ff655e2c09f623fb81c835fec0db240a0a2bff4aebf82c660000804b0089000000800025670034013ecd7fb9578e027d9aff883c4e050515ffa567a41635000040258024800000400012b8f81f409f663fac094013ecd7fc83ddb02829affa480bc04fb02c6e0000804b09c5000000200035ea98a9604f60da6c7c113d505c35ffe941d409f65c05034c050500c9880004409bc800000006d2bd3cec813c40cde062c1fd400000200008791ea3db3cf380a009f666f05005813d80ffe0a0588c00040092106a00000000bc75cac009f66db54c04a813d80a100801241ed40000000078ebae3b6da7a008809e2050904008811f100000000bc72389009f60eca8002049c400000002f1b2ca3027d93a71fa813ec204bc400000002f1b2b34027b0397608880cd10000000039b8e1a51036820505080d51000000003a7461ed1036760505080dd1000000003b2f62311006260505160bca00000080002b785e2a80a0a6c028de728145037f1f9e456488000202b2540001022c1894000001000057058c5b81414d806dbcd4028a18f4df23a050502c8d0000404b05a5000000800035b6471bc05053602431f380a2864087bdb0141458064ab0d6c00053fc013ec0b0680006012c15940000020000d6c06c6581414d807fb972028a1901d78dc050536020ec1800a0a6c039d639813d80b0780006012c1494000002000096ab8c6581414d8062be32028a1b01417e04050a360172d77009e2058440003009409c200000040006b3486a480a0a1cab7134c8117dcc02879b018fae2c050f3601ced54809e21012720000000067fbad0007e7e84045c80000000100661580958004041c8000000019f3658401cdfa2c0d64000002000144016c02c36ddfff0282984acc1ee05052c36f0ac02828669d82da8f821480a0a10f140002c8e0001004b03190000008000519fd190c43b2e0066108b08401428c342a0ce02828258a0604a6be959aee0e6050502c920001004b02d90000008000459fa164404fb30a8580a00a14619c306701414c32ce10e02829659081f814141029030164b000080200" , MessageType.BSM, ""); + return message; + } + + public static SpatDecodedMessage getSpatDecodedMessage(){ + SpatDecodedMessage message = new SpatDecodedMessage(MockSpatGenerator.getProcessedSpats().get(0),MockSpatGenerator.getJsonSpats().get(0),"001338000817a780000089680500204642b342b34802021a15a955a940181190acd0acd20100868555c555c00104342aae2aae002821a155715570" , MessageType.BSM, ""); + return message; + } + + public static TimDecodedMessage getTimDecodedMessage(){ + TimDecodedMessage message = new TimDecodedMessage(MockTimGenerator.getJsonTims().get(0),"005f498718cca69ec1a04600000100105d9b46ec5be401003a0103810040038081d4001f80d07016da410000000000000bbc2b0f775d9b0309c271431fa166ee0a27fff93f136b8205a0a107fb2ef979f4c5bfaeec97e4ad70c2fb36cd9730becdb355cc2fd2a7556b160b98b46ab98ae62c185fa55efb468d5b4000000004e2863f42cddc144ff7980040401262cdd7b809c509f5c62cdd35519c507b9062cdcee129c505cf262cdca5ff9c50432c62cdc5d3d9c502e3e62cdc13e79c501e9262cdbca2d9c5013ee62cdb80359c500e6a62cdb36299c500bc862cdaec1d9c50093c62cdaa2109c5006ea1080203091a859eeebb36006001830001aad27f4ff7580001aad355e39b5880a30029d6585009ef808332d8d9f80c3855151b38c772f765007967ec1170bcb7937f5cb880a25a52863493bcb87570dbcb5abc6bfb2faec606cfa34eb95a24790b2017366d3aabe7729e" , MessageType.TIM, ""); + return message; + } + + public static SrmDecodedMessage getSrmDecodedMessage(){ + SrmDecodedMessage message = new SrmDecodedMessage(MockSrmGenerator.getJsonSrms().get(0),"001d2130000010090bd341080d00855c6c0c6899853000a534f7c24cb29897694759b7c0" , MessageType.SRM, ""); + return message; + } + + public static SsmDecodedMessage getSsmDecodedMessage(){ + SsmDecodedMessage message = new SsmDecodedMessage(MockSsmGenerator.getJsonSsms().get(0),"001e120000000005e9c04071a26614c06000040ba0" , MessageType.SSM, ""); + return message; + } +} diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockMapGenerator.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockMapGenerator.java index aaf36815..5529b4c8 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockMapGenerator.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockMapGenerator.java @@ -4,25 +4,51 @@ import java.util.List; 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.geojsonconverter.pojos.geojson.LineString; import us.dot.its.jpo.geojsonconverter.pojos.geojson.map.ProcessedMap; +import us.dot.its.jpo.ode.model.OdeMapData; public class MockMapGenerator { - static String mapString = "{\"properties\":{\"messageType\":\"MAP\",\"odeReceivedAt\":\"2022-06-17T19:02:12.333377Z\",\"originIp\":\"10.11.81.12\",\"intersectionId\":12109,\"msgIssueRevision\":2,\"revision\":2,\"refPoint\":{\"latitude\":39.5880413,\"longitude\":-105.0908854,\"elevation\":1691.0},\"cti4501Conformant\":false,\"validationMessages\":[{\"message\":\"$.metadata.@class: is missing but it is required\",\"jsonPath\":\"$.metadata\",\"schemaPath\":\"#/$defs/OdeMapMetadata/required\"},{\"message\":\"$.payload.@class: is missing but it is required\",\"jsonPath\":\"$.payload\",\"schemaPath\":\"#/$defs/OdeMapPayload/required\"},{\"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\":\"2022-06-17T19:02:12.333377Z\"},\"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],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[723,-3116],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[892,-3818],\"stopLine\":null,\"dwidth\":null,\"delevation\":20},{\"delta\":[702,-2507],\"stopLine\":null,\"dwidth\":null,\"delevation\":20},{\"delta\":[681,-2412],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[827,-2798],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[1176,-3614],\"stopLine\":null,\"dwidth\":null,\"delevation\":20},{\"delta\":[683,-1992],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[1000,-2818],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[466,-1295],\"stopLine\":null,\"dwidth\":null,\"delevation\":20},{\"delta\":[2505,-6164],\"stopLine\":null,\"dwidth\":null,\"delevation\":20},{\"delta\":[6037,-13380],\"stopLine\":null,\"dwidth\":null,\"delevation\":70}],\"laneId\":1,\"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}},\"remoteIntersection\":null,\"signalGroup\":2,\"userClass\":null,\"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],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[807,-3733],\"stopLine\":null,\"dwidth\":null,\"delevation\":30},{\"delta\":[1010,-4226],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[1612,-5477],\"stopLine\":null,\"dwidth\":null,\"delevation\":30},{\"delta\":[1142,-3648],\"stopLine\":null,\"dwidth\":null,\"delevation\":20},{\"delta\":[1131,-3343],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[2259,-6170],\"stopLine\":null,\"dwidth\":null,\"delevation\":30},{\"delta\":[2359,-5737],\"stopLine\":null,\"dwidth\":null,\"delevation\":30},{\"delta\":[5300,-11774],\"stopLine\":null,\"dwidth\":null,\"delevation\":50}],\"laneId\":2,\"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}},\"remoteIntersection\":null,\"signalGroup\":2,\"userClass\":null,\"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],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[380,-1813],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[832,-3451],\"stopLine\":null,\"dwidth\":null,\"delevation\":30},{\"delta\":[202,-872],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[340,-482],\"stopLine\":null,\"dwidth\":null,\"delevation\":-10}],\"laneId\":3,\"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}},\"remoteIntersection\":null,\"signalGroup\":2,\"userClass\":null,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":6,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910008,39.5878477],[-105.0909927,39.5878181]]},\"properties\":{\"nodes\":[{\"delta\":[-988,-2151],\"stopLine\":null,\"dwidth\":null,\"delevation\":20},{\"delta\":[69,-329],\"stopLine\":null,\"dwidth\":null,\"delevation\":null}],\"laneId\":6,\"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],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[76,-377],\"stopLine\":null,\"dwidth\":null,\"delevation\":10}],\"laneId\":5,\"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],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[76,-349],\"stopLine\":null,\"dwidth\":null,\"delevation\":null}],\"laneId\":4,\"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],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[-357,-96],\"stopLine\":null,\"dwidth\":null,\"delevation\":null}],\"laneId\":10,\"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],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[-2644,-581],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[-2887,-442],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[-3583,-339],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[-3757,27],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[-3249,361],\"stopLine\":null,\"dwidth\":null,\"delevation\":-10},{\"delta\":[-2050,478],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[-3893,1184],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[-2625,766],\"stopLine\":null,\"dwidth\":null,\"delevation\":-10},{\"delta\":[-2524,607],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[-2280,325],\"stopLine\":null,\"dwidth\":null,\"delevation\":10}],\"laneId\":8,\"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}},\"remoteIntersection\":null,\"signalGroup\":4,\"userClass\":null,\"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],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[-2322,-471],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[-1877,-349],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[-1787,-235],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[-2666,-102],\"stopLine\":null,\"dwidth\":null,\"delevation\":10}],\"laneId\":7,\"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}},\"remoteIntersection\":null,\"signalGroup\":null,\"userClass\":null,\"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],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[-2420,-499],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[-1968,-339],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[-1843,-256],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[-2121,-339],\"stopLine\":null,\"dwidth\":null,\"delevation\":null}],\"laneId\":9,\"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}},\"remoteIntersection\":null,\"signalGroup\":4,\"userClass\":null,\"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],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[-885,4854],\"stopLine\":null,\"dwidth\":null,\"delevation\":-30},{\"delta\":[-410,2559],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[-333,1847],\"stopLine\":null,\"dwidth\":null,\"delevation\":-10},{\"delta\":[-319,3244],\"stopLine\":null,\"dwidth\":null,\"delevation\":-20},{\"delta\":[-500,4510],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[-333,3403],\"stopLine\":null,\"dwidth\":null,\"delevation\":-30},{\"delta\":[-268,2982],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[-758,11212],\"stopLine\":null,\"dwidth\":null,\"delevation\":-30},{\"delta\":[-323,11176],\"stopLine\":null,\"dwidth\":null,\"delevation\":-70}],\"laneId\":12,\"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}},\"remoteIntersection\":null,\"signalGroup\":6,\"userClass\":null,\"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],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[-896,4816],\"stopLine\":null,\"dwidth\":null,\"delevation\":-30},{\"delta\":[-326,2227],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[-333,2366],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[-410,3030],\"stopLine\":null,\"dwidth\":null,\"delevation\":-20},{\"delta\":[-326,3009],\"stopLine\":null,\"dwidth\":null,\"delevation\":-10},{\"delta\":[-271,2518],\"stopLine\":null,\"dwidth\":null,\"delevation\":-10},{\"delta\":[-264,2857],\"stopLine\":null,\"dwidth\":null,\"delevation\":-20},{\"delta\":[-243,2504],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[-740,11165],\"stopLine\":null,\"dwidth\":null,\"delevation\":-30},{\"delta\":[-253,11359],\"stopLine\":null,\"dwidth\":null,\"delevation\":-70}],\"laneId\":13,\"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}},\"remoteIntersection\":null,\"signalGroup\":6,\"userClass\":null,\"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],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[-563,3136],\"stopLine\":null,\"dwidth\":null,\"delevation\":-20},{\"delta\":[-352,2336],\"stopLine\":null,\"dwidth\":null,\"delevation\":-10},{\"delta\":[-223,1407],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[-155,1778],\"stopLine\":null,\"dwidth\":null,\"delevation\":null}],\"laneId\":11,\"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}},\"remoteIntersection\":null,\"signalGroup\":null,\"userClass\":null,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":14,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0908389,39.5882151],[-105.0908478,39.5882471]]},\"properties\":{\"nodes\":[{\"delta\":[398,1931],\"stopLine\":null,\"dwidth\":null,\"delevation\":-10},{\"delta\":[-76,356],\"stopLine\":null,\"dwidth\":null,\"delevation\":null}],\"laneId\":14,\"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],\"stopLine\":null,\"dwidth\":null,\"delevation\":-20},{\"delta\":[-89,349],\"stopLine\":null,\"dwidth\":null,\"delevation\":null}],\"laneId\":15,\"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\":{\"signalGroupId\":null,\"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\":{\"signalGroupId\":null,\"ingressLaneId\":11,\"egressLaneId\":10}}]}}"; + static TypeReference> typeReference = new TypeReference<>(){}; + static String processedMapString = "{\"properties\":{\"messageType\":\"MAP\",\"odeReceivedAt\":\"2022-06-17T19:02:12.333377Z\",\"originIp\":\"10.11.81.12\",\"intersectionId\":12109,\"msgIssueRevision\":2,\"revision\":2,\"refPoint\":{\"latitude\":39.5880413,\"longitude\":-105.0908854,\"elevation\":1691.0},\"cti4501Conformant\":false,\"validationMessages\":[{\"message\":\"$.metadata.@class: is missing but it is required\",\"jsonPath\":\"$.metadata\",\"schemaPath\":\"#/$defs/OdeMapMetadata/required\"},{\"message\":\"$.payload.@class: is missing but it is required\",\"jsonPath\":\"$.payload\",\"schemaPath\":\"#/$defs/OdeMapPayload/required\"},{\"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\":\"2022-06-17T19:02:12.333377Z\"},\"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],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[723,-3116],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[892,-3818],\"stopLine\":null,\"dwidth\":null,\"delevation\":20},{\"delta\":[702,-2507],\"stopLine\":null,\"dwidth\":null,\"delevation\":20},{\"delta\":[681,-2412],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[827,-2798],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[1176,-3614],\"stopLine\":null,\"dwidth\":null,\"delevation\":20},{\"delta\":[683,-1992],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[1000,-2818],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[466,-1295],\"stopLine\":null,\"dwidth\":null,\"delevation\":20},{\"delta\":[2505,-6164],\"stopLine\":null,\"dwidth\":null,\"delevation\":20},{\"delta\":[6037,-13380],\"stopLine\":null,\"dwidth\":null,\"delevation\":70}],\"laneId\":1,\"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}},\"remoteIntersection\":null,\"signalGroup\":2,\"userClass\":null,\"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],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[807,-3733],\"stopLine\":null,\"dwidth\":null,\"delevation\":30},{\"delta\":[1010,-4226],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[1612,-5477],\"stopLine\":null,\"dwidth\":null,\"delevation\":30},{\"delta\":[1142,-3648],\"stopLine\":null,\"dwidth\":null,\"delevation\":20},{\"delta\":[1131,-3343],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[2259,-6170],\"stopLine\":null,\"dwidth\":null,\"delevation\":30},{\"delta\":[2359,-5737],\"stopLine\":null,\"dwidth\":null,\"delevation\":30},{\"delta\":[5300,-11774],\"stopLine\":null,\"dwidth\":null,\"delevation\":50}],\"laneId\":2,\"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}},\"remoteIntersection\":null,\"signalGroup\":2,\"userClass\":null,\"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],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[380,-1813],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[832,-3451],\"stopLine\":null,\"dwidth\":null,\"delevation\":30},{\"delta\":[202,-872],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[340,-482],\"stopLine\":null,\"dwidth\":null,\"delevation\":-10}],\"laneId\":3,\"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}},\"remoteIntersection\":null,\"signalGroup\":2,\"userClass\":null,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":6,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0910008,39.5878477],[-105.0909927,39.5878181]]},\"properties\":{\"nodes\":[{\"delta\":[-988,-2151],\"stopLine\":null,\"dwidth\":null,\"delevation\":20},{\"delta\":[69,-329],\"stopLine\":null,\"dwidth\":null,\"delevation\":null}],\"laneId\":6,\"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],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[76,-377],\"stopLine\":null,\"dwidth\":null,\"delevation\":10}],\"laneId\":5,\"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],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[76,-349],\"stopLine\":null,\"dwidth\":null,\"delevation\":null}],\"laneId\":4,\"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],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[-357,-96],\"stopLine\":null,\"dwidth\":null,\"delevation\":null}],\"laneId\":10,\"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],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[-2644,-581],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[-2887,-442],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[-3583,-339],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[-3757,27],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[-3249,361],\"stopLine\":null,\"dwidth\":null,\"delevation\":-10},{\"delta\":[-2050,478],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[-3893,1184],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[-2625,766],\"stopLine\":null,\"dwidth\":null,\"delevation\":-10},{\"delta\":[-2524,607],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[-2280,325],\"stopLine\":null,\"dwidth\":null,\"delevation\":10}],\"laneId\":8,\"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}},\"remoteIntersection\":null,\"signalGroup\":4,\"userClass\":null,\"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],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[-2322,-471],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[-1877,-349],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[-1787,-235],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[-2666,-102],\"stopLine\":null,\"dwidth\":null,\"delevation\":10}],\"laneId\":7,\"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}},\"remoteIntersection\":null,\"signalGroup\":null,\"userClass\":null,\"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],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[-2420,-499],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[-1968,-339],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[-1843,-256],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[-2121,-339],\"stopLine\":null,\"dwidth\":null,\"delevation\":null}],\"laneId\":9,\"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}},\"remoteIntersection\":null,\"signalGroup\":4,\"userClass\":null,\"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],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[-885,4854],\"stopLine\":null,\"dwidth\":null,\"delevation\":-30},{\"delta\":[-410,2559],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[-333,1847],\"stopLine\":null,\"dwidth\":null,\"delevation\":-10},{\"delta\":[-319,3244],\"stopLine\":null,\"dwidth\":null,\"delevation\":-20},{\"delta\":[-500,4510],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[-333,3403],\"stopLine\":null,\"dwidth\":null,\"delevation\":-30},{\"delta\":[-268,2982],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[-758,11212],\"stopLine\":null,\"dwidth\":null,\"delevation\":-30},{\"delta\":[-323,11176],\"stopLine\":null,\"dwidth\":null,\"delevation\":-70}],\"laneId\":12,\"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}},\"remoteIntersection\":null,\"signalGroup\":6,\"userClass\":null,\"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],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[-896,4816],\"stopLine\":null,\"dwidth\":null,\"delevation\":-30},{\"delta\":[-326,2227],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[-333,2366],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[-410,3030],\"stopLine\":null,\"dwidth\":null,\"delevation\":-20},{\"delta\":[-326,3009],\"stopLine\":null,\"dwidth\":null,\"delevation\":-10},{\"delta\":[-271,2518],\"stopLine\":null,\"dwidth\":null,\"delevation\":-10},{\"delta\":[-264,2857],\"stopLine\":null,\"dwidth\":null,\"delevation\":-20},{\"delta\":[-243,2504],\"stopLine\":null,\"dwidth\":null,\"delevation\":null},{\"delta\":[-740,11165],\"stopLine\":null,\"dwidth\":null,\"delevation\":-30},{\"delta\":[-253,11359],\"stopLine\":null,\"dwidth\":null,\"delevation\":-70}],\"laneId\":13,\"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}},\"remoteIntersection\":null,\"signalGroup\":6,\"userClass\":null,\"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],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[-563,3136],\"stopLine\":null,\"dwidth\":null,\"delevation\":-20},{\"delta\":[-352,2336],\"stopLine\":null,\"dwidth\":null,\"delevation\":-10},{\"delta\":[-223,1407],\"stopLine\":null,\"dwidth\":null,\"delevation\":10},{\"delta\":[-155,1778],\"stopLine\":null,\"dwidth\":null,\"delevation\":null}],\"laneId\":11,\"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}},\"remoteIntersection\":null,\"signalGroup\":null,\"userClass\":null,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":14,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0908389,39.5882151],[-105.0908478,39.5882471]]},\"properties\":{\"nodes\":[{\"delta\":[398,1931],\"stopLine\":null,\"dwidth\":null,\"delevation\":-10},{\"delta\":[-76,356],\"stopLine\":null,\"dwidth\":null,\"delevation\":null}],\"laneId\":14,\"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],\"stopLine\":null,\"dwidth\":null,\"delevation\":-20},{\"delta\":[-89,349],\"stopLine\":null,\"dwidth\":null,\"delevation\":null}],\"laneId\":15,\"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\":{\"signalGroupId\":null,\"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\":{\"signalGroupId\":null,\"ingressLaneId\":11,\"egressLaneId\":10}}]}}"; - public static List getProcessedMaps(){ + + public static String mapString = "{\"metadata\":{\"logFileName\":\"\",\"recordType\":\"mapTx\",\"securityResultCode\":\"success\",\"receivedMessageDetails\":{\"rxSource\":\"NA\"},\"payloadType\":\"us.dot.its.jpo.ode.model.OdeMapPayload\",\"serialId\":{\"streamId\":\"2c35bf06-4776-49e0-a1da-750347cc0804\",\"bundleSize\":1,\"bundleId\":0,\"recordId\":0,\"serialNumber\":0},\"odeReceivedAt\":\"2024-05-09T17:09:49.613644024Z\",\"schemaVersion\":6,\"maxDurationTime\":0,\"recordGeneratedAt\":\"\",\"sanitized\":false,\"odePacketID\":\"\",\"odeTimStartDateTime\":\"\",\"mapSource\":\"RSU\",\"originIp\":\"172.18.0.1\"},\"payload\":{\"data\":{\"msgIssueRevision\":0,\"layerType\":\"intersectionData\",\"layerID\":0,\"intersections\":{\"intersectionGeometry\":[{\"id\":{\"id\":12110},\"revision\":0,\"refPoint\":{\"latitude\":39.5952649,\"longitude\":-105.0914122,\"elevation\":1677.0},\"laneWidth\":366,\"laneSet\":{\"GenericLane\":[{\"laneID\":2,\"ingressApproach\":1,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":2225,\"y\":808}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":10517,\"y\":-161}},\"attributes\":{\"dElevation\":-60}},{\"delta\":{\"nodeXY6\":{\"x\":2769,\"y\":112}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":6142,\"y\":-180}},\"attributes\":{\"dElevation\":-30}},{\"delta\":{\"nodeXY6\":{\"x\":6636,\"y\":-12}},\"attributes\":{\"dElevation\":-20}},{\"delta\":{\"nodeXY6\":{\"x\":3804,\"y\":-7}}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":19,\"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\":4,\"connectionID\":1}]}},{\"laneID\":3,\"ingressApproach\":1,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":2222,\"y\":515}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":2933,\"y\":-82}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":6259,\"y\":-85}},\"attributes\":{\"dElevation\":-40}},{\"delta\":{\"nodeXY6\":{\"x\":3416,\"y\":-98}},\"attributes\":{\"dElevation\":-20}},{\"delta\":{\"nodeXY6\":{\"x\":6867,\"y\":-39}},\"attributes\":{\"dElevation\":-30}},{\"delta\":{\"nodeXY6\":{\"x\":2657,\"y\":-55}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":4259,\"y\":67}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":3481,\"y\":0}}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":18,\"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\":4,\"connectionID\":1}]}},{\"laneID\":1,\"ingressApproach\":1,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":2215,\"y\":1218}}},{\"delta\":{\"nodeXY6\":{\"x\":10451,\"y\":-223}},\"attributes\":{\"dElevation\":-70}},{\"delta\":{\"nodeXY6\":{\"x\":1852,\"y\":-108}},\"attributes\":{\"dElevation\":-10}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":28,\"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}]}},{\"laneID\":4,\"ingressApproach\":1,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":2213,\"y\":-124}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":7187,\"y\":20}},\"attributes\":{\"dElevation\":-40}},{\"delta\":{\"nodeXY6\":{\"x\":541,\"y\":40}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":1298,\"y\":229}}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":12,\"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\":7,\"connectionID\":1}]}},{\"laneID\":6,\"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}}},\"nodeList\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":2267,\"y\":-1019}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":365,\"y\":7}}}]}}},{\"laneID\":5,\"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}}},\"nodeList\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":2267,\"y\":-662}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":338,\"y\":0}}}]}}},{\"laneID\":7,\"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}}},\"nodeList\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":2294,\"y\":-1463}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":365,\"y\":27}}}]}}},{\"laneID\":12,\"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}}},\"nodeList\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-952,\"y\":-1898}}},{\"delta\":{\"nodeXY6\":{\"x\":0,\"y\":-377}}}]}}},{\"laneID\":13,\"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}}},\"nodeList\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-1440,\"y\":-1898}}},{\"delta\":{\"nodeXY6\":{\"x\":7,\"y\":-390}}},{\"delta\":{\"nodeXY6\":{\"x\":0,\"y\":0}}},{\"delta\":{\"nodeXY6\":{\"x\":0,\"y\":7}}}]}}},{\"laneID\":10,\"ingressApproach\":3,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":705,\"y\":-1919}}},{\"delta\":{\"nodeXY6\":{\"x\":-1,\"y\":-7515}},\"attributes\":{\"dElevation\":-40}},{\"delta\":{\"nodeXY6\":{\"x\":-41,\"y\":-5272}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":-17,\"y\":-11337}}},{\"delta\":{\"nodeXY6\":{\"x\":17,\"y\":-5938}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":0,\"y\":0}}},{\"delta\":{\"nodeXY6\":{\"x\":-7,\"y\":-7}}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":26,\"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}]}},{\"laneID\":9,\"ingressApproach\":3,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":1090,\"y\":-1930}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":9,\"y\":-4764}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":-20,\"y\":-6402}},\"attributes\":{\"dElevation\":-20}},{\"delta\":{\"nodeXY6\":{\"x\":-34,\"y\":-7953}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":16,\"y\":-10940}},\"attributes\":{\"dElevation\":20}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":27,\"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}]}},{\"laneID\":11,\"ingressApproach\":3,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":350,\"y\":-1925}}},{\"delta\":{\"nodeXY6\":{\"x\":-32,\"y\":-6698}},\"attributes\":{\"dElevation\":-30}},{\"delta\":{\"nodeXY6\":{\"x\":156,\"y\":-2616}}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":18,\"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\":1,\"connectionID\":1}]}},{\"laneID\":8,\"ingressApproach\":3,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":1557,\"y\":-1956}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":-101,\"y\":-1956}}},{\"delta\":{\"nodeXY6\":{\"x\":-5,\"y\":-7393}},\"attributes\":{\"dElevation\":-40}},{\"delta\":{\"nodeXY6\":{\"x\":-121,\"y\":-1636}}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":7,\"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}]}},{\"laneID\":15,\"ingressApproach\":5,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-2405,\"y\":-1018}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":-9339,\"y\":26}},\"attributes\":{\"dElevation\":30}},{\"delta\":{\"nodeXY6\":{\"x\":-11895,\"y\":-38}},\"attributes\":{\"dElevation\":50}},{\"delta\":{\"nodeXY6\":{\"x\":-8814,\"y\":13}}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":6,\"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\":8,\"connectionID\":1}]}},{\"laneID\":16,\"ingressApproach\":5,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-2337,\"y\":-656}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":-12229,\"y\":15}},\"attributes\":{\"dElevation\":50}},{\"delta\":{\"nodeXY6\":{\"x\":-11362,\"y\":-37}},\"attributes\":{\"dElevation\":30}},{\"delta\":{\"nodeXY6\":{\"x\":-6537,\"y\":1}}}]}},\"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}},\"signalGroup\":8,\"connectionID\":1}]}},{\"laneID\":17,\"ingressApproach\":5,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-2338,\"y\":-284}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":-8892,\"y\":-18}},\"attributes\":{\"dElevation\":30}},{\"delta\":{\"nodeXY6\":{\"x\":-11959,\"y\":-2}},\"attributes\":{\"dElevation\":50}},{\"delta\":{\"nodeXY6\":{\"x\":-1564,\"y\":-180}}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":26,\"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\":3,\"connectionID\":1}]}},{\"laneID\":14,\"ingressApproach\":5,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-2401,\"y\":-1380}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":-19947,\"y\":8}},\"attributes\":{\"dElevation\":80}},{\"delta\":{\"nodeXY6\":{\"x\":-10108,\"y\":-7}}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":13,\"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}]}},{\"laneID\":18,\"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}}},\"nodeList\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-2264,\"y\":479}},\"attributes\":{\"dElevation\":20}},{\"delta\":{\"nodeXY6\":{\"x\":-580,\"y\":-14}}}]}}},{\"laneID\":19,\"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}}},\"nodeList\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-2257,\"y\":796}},\"attributes\":{\"dElevation\":20}},{\"delta\":{\"nodeXY6\":{\"x\":-518,\"y\":0}}}]}}},{\"laneID\":20,\"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}}},\"nodeList\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-2236,\"y\":1229}},\"attributes\":{\"dElevation\":20}},{\"delta\":{\"nodeXY6\":{\"x\":-484,\"y\":0}}}]}}},{\"laneID\":23,\"ingressApproach\":7,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-914,\"y\":1903}},\"attributes\":{\"dElevation\":20}},{\"delta\":{\"nodeXY6\":{\"x\":225,\"y\":12288}},\"attributes\":{\"dElevation\":30}},{\"delta\":{\"nodeXY6\":{\"x\":40,\"y\":9634}},\"attributes\":{\"dElevation\":-20}},{\"delta\":{\"nodeXY6\":{\"x\":32,\"y\":8035}},\"attributes\":{\"dElevation\":-30}},{\"delta\":{\"nodeXY6\":{\"x\":0,\"y\":0}}},{\"delta\":{\"nodeXY6\":{\"x\":-7,\"y\":28}}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":12,\"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}]}},{\"laneID\":22,\"ingressApproach\":7,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-1221,\"y\":1914}},\"attributes\":{\"dElevation\":20}},{\"delta\":{\"nodeXY6\":{\"x\":92,\"y\":10475}},\"attributes\":{\"dElevation\":30}},{\"delta\":{\"nodeXY6\":{\"x\":89,\"y\":11582}},\"attributes\":{\"dElevation\":-20}},{\"delta\":{\"nodeXY6\":{\"x\":48,\"y\":7912}},\"attributes\":{\"dElevation\":-30}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":13,\"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}]}},{\"laneID\":21,\"ingressApproach\":7,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-1649,\"y\":1935}},\"attributes\":{\"dElevation\":20}},{\"delta\":{\"nodeXY6\":{\"x\":16,\"y\":3182}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":21,\"y\":5991}},\"attributes\":{\"dElevation\":20}},{\"delta\":{\"nodeXY6\":{\"x\":102,\"y\":4810}}},{\"delta\":{\"nodeXY6\":{\"x\":85,\"y\":11803}},\"attributes\":{\"dElevation\":-30}},{\"delta\":{\"nodeXY6\":{\"x\":19,\"y\":4161}},\"attributes\":{\"dElevation\":-20}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":20,\"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}]}},{\"laneID\":24,\"ingressApproach\":7,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-535,\"y\":1926}},\"attributes\":{\"dElevation\":20}},{\"delta\":{\"nodeXY6\":{\"x\":28,\"y\":4582}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":88,\"y\":3793}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":-143,\"y\":1866}},\"attributes\":{\"dElevation\":10}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":6,\"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\":5,\"connectionID\":1}]}},{\"laneID\":25,\"ingressApproach\":7,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-213,\"y\":1913}},\"attributes\":{\"dElevation\":20}},{\"delta\":{\"nodeXY6\":{\"x\":14,\"y\":4594}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":-91,\"y\":2238}},\"attributes\":{\"dElevation\":10}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":5,\"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\":5,\"connectionID\":1}]}},{\"laneID\":26,\"egressApproach\":8,\"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}}},\"nodeList\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":724,\"y\":1902}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":-7,\"y\":352}},\"attributes\":{\"dElevation\":10}}]}}},{\"laneID\":27,\"egressApproach\":8,\"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}}},\"nodeList\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":1105,\"y\":1888}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":-7,\"y\":332}},\"attributes\":{\"dElevation\":10}}]}}},{\"laneID\":28,\"egressApproach\":8,\"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}}},\"nodeList\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":1591,\"y\":1874}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":-14,\"y\":346}}},{\"delta\":{\"nodeXY6\":{\"x\":0,\"y\":7}}}]}}}]}}]}},\"dataType\":\"us.dot.its.jpo.ode.plugin.j2735.J2735MAP\"}}"; + + public static List> getProcessedMaps(){ + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerModule(new JavaTimeModule()); + + ArrayList> maps = new ArrayList<>(); + + try { + ProcessedMap map = objectMapper.readValue(processedMapString, typeReference); + maps.add(map); + } catch (JsonMappingException e) { + System.out.println("A Json Mapping Exception Occurred while trying to get data from mocked map."); + e.printStackTrace(); + } catch (JsonProcessingException e) { + System.out.println("A Json Processing Exception Occurred while trying to get data from a mocked map."); + e.printStackTrace(); + } + return maps; + } + + public static List getJsonMaps(){ ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(new JavaTimeModule()); - ArrayList maps = new ArrayList<>(); + ArrayList maps = new ArrayList<>(); try { - ProcessedMap map = objectMapper.readValue(mapString, ProcessedMap.class); + OdeMapData map = objectMapper.readValue(mapString, OdeMapData.class); maps.add(map); } catch (JsonMappingException e) { System.out.println("A Json Mapping Exception Occurred while trying to get data from mocked map."); diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockSpatGenerator.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockSpatGenerator.java index 6de23300..e5676580 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockSpatGenerator.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockSpatGenerator.java @@ -8,9 +8,11 @@ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import us.dot.its.jpo.geojsonconverter.pojos.spat.ProcessedSpat; +import us.dot.its.jpo.ode.model.OdeSpatData; public class MockSpatGenerator { - static String spatString = "{\"messageType\":\"SPAT\",\"odeReceivedAt\":\"2022-06-17T19:15:13.671068Z\",\"originIp\":\"10.11.81.12\",\"intersectionId\":12109,\"cti4501Conformant\":false,\"validationMessages\":[{\"message\":\"$.metadata.@class: is missing but it is required\",\"jsonPath\":\"$.metadata\",\"schemaPath\":\"#/$defs/OdeSpatMetadata/required\"},{\"message\":\"$.payload.@class: is missing but it is required\",\"jsonPath\":\"$.payload\",\"schemaPath\":\"#/$defs/OdeSpatPayload/required\"},{\"message\":\"$.payload.data.timeStamp: null found, integer expected\",\"jsonPath\":\"$.payload.data.timeStamp\",\"schemaPath\":\"#/$defs/J2735MinuteOfTheYear/type\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].id.region\",\"schemaPath\":\"#/$defs/J2735RoadRegulatorID/type\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime: null found, integer expected\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime\",\"schemaPath\":\"#/$defs/J2735TimeMark/type\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime: null found, integer expected\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime\",\"schemaPath\":\"#/$defs/J2735TimeMark/type\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime: null found, integer expected\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime\",\"schemaPath\":\"#/$defs/J2735TimeMark/type\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime: null found, integer expected\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime\",\"schemaPath\":\"#/$defs/J2735TimeMark/type\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime: null found, integer expected\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime\",\"schemaPath\":\"#/$defs/J2735TimeMark/type\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime: null found, integer expected\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime\",\"schemaPath\":\"#/$defs/J2735TimeMark/type\"}],\"revision\":0,\"status\":{\"manualControlIsEnabled\":true,\"stopTimeIsActivated\":false,\"failureFlash\":true,\"preemptIsActive\":false,\"signalPriorityIsActive\":false,\"fixedTimeOperation\":false,\"trafficDependentOperation\":false,\"standbyOperation\":false,\"failureMode\":false,\"off\":false,\"recentMAPmessageUpdate\":false,\"recentChangeInMAPassignedLanesIDsUsed\":false,\"noValidMAPisAvailableAtThisTime\":false,\"noValidSPATisAvailableAtThisTime\":false},\"utcTimeStamp\":\"2022-06-17T19:15:13.745Z\",\"enabledLanes\":[],\"states\":[{\"signalGroup\":2,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"startTime\":null,\"minEndTime\":\"2022-06-17T19:15:12.5Z\",\"maxEndTime\":\"2022-06-17T19:15:12.5Z\",\"likelyTime\":null,\"confidence\":null,\"nextTime\":null}}]},{\"signalGroup\":4,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"startTime\":null,\"minEndTime\":\"2022-06-17T19:15:12.2Z\",\"maxEndTime\":\"2022-06-17T19:15:12.2Z\",\"likelyTime\":null,\"confidence\":null,\"nextTime\":null}}]},{\"signalGroup\":6,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"startTime\":null,\"minEndTime\":\"2022-06-17T19:15:12.5Z\",\"maxEndTime\":\"2022-06-17T19:15:12.5Z\",\"likelyTime\":null,\"confidence\":null,\"nextTime\":null}}]}]}"; + static String processedSpatString = "{\"messageType\":\"SPAT\",\"odeReceivedAt\":\"2022-06-17T19:15:13.671068Z\",\"originIp\":\"10.11.81.12\",\"intersectionId\":12109,\"cti4501Conformant\":false,\"validationMessages\":[{\"message\":\"$.metadata.@class: is missing but it is required\",\"jsonPath\":\"$.metadata\",\"schemaPath\":\"#/$defs/OdeSpatMetadata/required\"},{\"message\":\"$.payload.@class: is missing but it is required\",\"jsonPath\":\"$.payload\",\"schemaPath\":\"#/$defs/OdeSpatPayload/required\"},{\"message\":\"$.payload.data.timeStamp: null found, integer expected\",\"jsonPath\":\"$.payload.data.timeStamp\",\"schemaPath\":\"#/$defs/J2735MinuteOfTheYear/type\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].id.region\",\"schemaPath\":\"#/$defs/J2735RoadRegulatorID/type\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime: null found, integer expected\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime\",\"schemaPath\":\"#/$defs/J2735TimeMark/type\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime: null found, integer expected\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime\",\"schemaPath\":\"#/$defs/J2735TimeMark/type\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime: null found, integer expected\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime\",\"schemaPath\":\"#/$defs/J2735TimeMark/type\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime: null found, integer expected\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime\",\"schemaPath\":\"#/$defs/J2735TimeMark/type\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime: null found, integer expected\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime\",\"schemaPath\":\"#/$defs/J2735TimeMark/type\"},{\"message\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime: null found, integer expected\",\"jsonPath\":\"$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime\",\"schemaPath\":\"#/$defs/J2735TimeMark/type\"}],\"revision\":0,\"status\":{\"manualControlIsEnabled\":true,\"stopTimeIsActivated\":false,\"failureFlash\":true,\"preemptIsActive\":false,\"signalPriorityIsActive\":false,\"fixedTimeOperation\":false,\"trafficDependentOperation\":false,\"standbyOperation\":false,\"failureMode\":false,\"off\":false,\"recentMAPmessageUpdate\":false,\"recentChangeInMAPassignedLanesIDsUsed\":false,\"noValidMAPisAvailableAtThisTime\":false,\"noValidSPATisAvailableAtThisTime\":false},\"utcTimeStamp\":\"2022-06-17T19:15:13.745Z\",\"enabledLanes\":[],\"states\":[{\"signalGroup\":2,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"startTime\":null,\"minEndTime\":\"2022-06-17T19:15:12.5Z\",\"maxEndTime\":\"2022-06-17T19:15:12.5Z\",\"likelyTime\":null,\"confidence\":null,\"nextTime\":null}}]},{\"signalGroup\":4,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"startTime\":null,\"minEndTime\":\"2022-06-17T19:15:12.2Z\",\"maxEndTime\":\"2022-06-17T19:15:12.2Z\",\"likelyTime\":null,\"confidence\":null,\"nextTime\":null}}]},{\"signalGroup\":6,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"startTime\":null,\"minEndTime\":\"2022-06-17T19:15:12.5Z\",\"maxEndTime\":\"2022-06-17T19:15:12.5Z\",\"likelyTime\":null,\"confidence\":null,\"nextTime\":null}}]}]}"; + static String spatString = "{\"metadata\":{\"logFileName\":\"\",\"recordType\":\"spatTx\",\"securityResultCode\":\"success\",\"receivedMessageDetails\":{\"rxSource\":\"NA\"},\"payloadType\":\"us.dot.its.jpo.ode.model.OdeSpatPayload\",\"serialId\":{\"streamId\":\"1c0a9b7e-b4c2-41db-bbb0-315101331a4f\",\"bundleSize\":1,\"bundleId\":0,\"recordId\":0,\"serialNumber\":0},\"odeReceivedAt\":\"2024-05-09T17:16:05.703050114Z\",\"schemaVersion\":6,\"maxDurationTime\":0,\"recordGeneratedAt\":\"\",\"sanitized\":false,\"odePacketID\":\"\",\"odeTimStartDateTime\":\"\",\"spatSource\":\"V2X\",\"originIp\":\"172.18.0.1\",\"isCertPresent\":false},\"payload\":{\"data\":{\"intersectionStateList\":{\"intersectionStatelist\":[{\"id\":{\"id\":12111},\"revision\":0,\"status\":{\"failureFlash\":false,\"noValidSPATisAvailableAtThisTime\":false,\"fixedTimeOperation\":false,\"standbyOperation\":false,\"trafficDependentOperation\":false,\"manualControlIsEnabled\":false,\"off\":false,\"stopTimeIsActivated\":false,\"recentChangeInMAPassignedLanesIDsUsed\":false,\"recentMAPmessageUpdate\":false,\"failureMode\":false,\"noValidMAPisAvailableAtThisTime\":false,\"signalPriorityIsActive\":false,\"preemptIsActive\":false},\"timeStamp\":35176,\"states\":{\"movementList\":[{\"signalGroup\":2,\"state_time_speed\":{\"movementEventList\":[{\"eventState\":\"PROTECTED_MOVEMENT_ALLOWED\",\"timing\":{\"minEndTime\":22120,\"maxEndTime\":22121}}]}},{\"signalGroup\":4,\"state_time_speed\":{\"movementEventList\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":22181,\"maxEndTime\":22181}}]}},{\"signalGroup\":6,\"state_time_speed\":{\"movementEventList\":[{\"eventState\":\"PROTECTED_MOVEMENT_ALLOWED\",\"timing\":{\"minEndTime\":22120,\"maxEndTime\":22121}}]}},{\"signalGroup\":8,\"state_time_speed\":{\"movementEventList\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":21852,\"maxEndTime\":21852}}]}},{\"signalGroup\":1,\"state_time_speed\":{\"movementEventList\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":21852,\"maxEndTime\":21852}}]}},{\"signalGroup\":5,\"state_time_speed\":{\"movementEventList\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":21852,\"maxEndTime\":21852}}]}}]}}]}},\"dataType\":\"us.dot.its.jpo.ode.plugin.j2735.J2735SPAT\"}}"; public static List getProcessedSpats(){ ObjectMapper objectMapper = new ObjectMapper(); @@ -19,7 +21,26 @@ public static List getProcessedSpats(){ ArrayList spats = new ArrayList<>(); try { - ProcessedSpat spat = objectMapper.readValue(spatString, ProcessedSpat.class); + ProcessedSpat spat = objectMapper.readValue(processedSpatString, ProcessedSpat.class); + spats.add(spat); + } catch (JsonMappingException e) { + System.out.println("A Json Mapping Exception Occurred while trying to get data from mocked spat."); + e.printStackTrace(); + } catch (JsonProcessingException e) { + System.out.println("A Json Processing Exception Occurred while trying to get data from a mocked spat."); + e.printStackTrace(); + } + return spats; + } + + public static List getJsonSpats(){ + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerModule(new JavaTimeModule()); + + ArrayList spats = new ArrayList<>(); + + try { + OdeSpatData spat = objectMapper.readValue(spatString, OdeSpatData.class); spats.add(spat); } catch (JsonMappingException e) { System.out.println("A Json Mapping Exception Occurred while trying to get data from mocked spat."); diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockSrmGenerator.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockSrmGenerator.java new file mode 100644 index 00000000..87a3cca8 --- /dev/null +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockSrmGenerator.java @@ -0,0 +1,39 @@ +package us.dot.its.jpo.ode.mockdata; + +import java.util.ArrayList; +import java.util.List; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + +import us.dot.its.jpo.ode.model.OdeSrmData; +public class MockSrmGenerator { + + static String srmString = "{\"metadata\":{\"logFileName\":\"\",\"recordType\":\"srmTx\",\"receivedMessageDetails\":{\"rxSource\":\"NA\"},\"payloadType\":\"us.dot.its.jpo.ode.model.OdeSrmPayload\",\"serialId\":{\"streamId\":\"79a1093a-7e2f-442c-b9ae-324b5a09c030\",\"bundleSize\":1,\"bundleId\":0,\"recordId\":0,\"serialNumber\":0},\"odeReceivedAt\":\"2024-05-09T15:17:48.020668755Z\",\"schemaVersion\":6,\"maxDurationTime\":0,\"recordGeneratedAt\":\"\",\"sanitized\":false,\"odePacketID\":\"\",\"odeTimStartDateTime\":\"\",\"originIp\":\"172.18.0.1\",\"srmSource\":\"RSU\"},\"payload\":{\"data\":{\"second\":0,\"sequenceNumber\":1,\"requests\":{\"signalRequestPackage\":[{\"request\":{\"id\":{\"id\":12109},\"requestID\":4,\"requestType\":\"priorityRequest\",\"inBoundLane\":{\"lane\":13},\"outBoundLane\":{\"lane\":4}},\"duration\":10979}]},\"requestor\":{\"id\":{\"stationID\":2366845094},\"type\":{\"role\":\"publicTransport\"},\"position\":{\"position\":{\"latitude\":39.5904915,\"longitude\":-105.0913829,\"elevation\":1685.4},\"heading\":175.9000}}},\"dataType\":\"us.dot.its.jpo.ode.plugin.j2735.J2735SRM\"}}"; + + public static List getJsonSrms(){ + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerModule(new JavaTimeModule()); + + ArrayList srms = new ArrayList<>(); + + try { + OdeSrmData srm = objectMapper.readValue(srmString, OdeSrmData.class); + srms.add(srm); + } catch (JsonMappingException e) { + System.out.println("A Json Mapping Exception Occurred while trying to get data from mocked srm."); + e.printStackTrace(); + } catch (JsonProcessingException e) { + System.out.println("A Json Processing Exception Occurred while trying to get data from a mocked srm."); + e.printStackTrace(); + } + return srms; + } + +} + + + + \ No newline at end of file diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockSsmGenerator.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockSsmGenerator.java new file mode 100644 index 00000000..5ebe61a6 --- /dev/null +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockSsmGenerator.java @@ -0,0 +1,39 @@ +package us.dot.its.jpo.ode.mockdata; + +import java.util.ArrayList; +import java.util.List; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + +import us.dot.its.jpo.ode.model.OdeSsmData; +public class MockSsmGenerator { + + static String ssmString = "{\"metadata\":{\"logFileName\":\"\",\"recordType\":\"ssmTx\",\"receivedMessageDetails\":{\"rxSource\":\"NA\"},\"payloadType\":\"us.dot.its.jpo.ode.model.OdeSsmPayload\",\"serialId\":{\"streamId\":\"e6f21a57-6952-402a-86c8-49e8fcb727c1\",\"bundleSize\":1,\"bundleId\":0,\"recordId\":0,\"serialNumber\":0},\"odeReceivedAt\":\"2024-05-09T15:27:17.557565954Z\",\"schemaVersion\":6,\"maxDurationTime\":0,\"recordGeneratedAt\":\"\",\"sanitized\":false,\"odePacketID\":\"\",\"odeTimStartDateTime\":\"\",\"originIp\":\"172.18.0.1\",\"ssmSource\":\"RSU\"},\"payload\":{\"data\":{\"second\":0,\"status\":{\"signalStatus\":[{\"sequenceNumber\":0,\"id\":{\"id\":12110},\"sigStatus\":{\"signalStatusPackage\":[{\"requester\":{\"id\":{\"stationID\":2366845094},\"request\":3,\"sequenceNumber\":0,\"typeData\":{\"role\":\"publicTransport\"}},\"inboundOn\":{\"lane\":23},\"status\":\"granted\"}]}}]}},\"dataType\":\"us.dot.its.jpo.ode.plugin.j2735.J2735SSM\"}}"; + + public static List getJsonSsms(){ + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerModule(new JavaTimeModule()); + + ArrayList ssms = new ArrayList<>(); + + try { + OdeSsmData ssm = objectMapper.readValue(ssmString, OdeSsmData.class); + ssms.add(ssm); + } catch (JsonMappingException e) { + System.out.println("A Json Mapping Exception Occurred while trying to get data from mocked ssm."); + e.printStackTrace(); + } catch (JsonProcessingException e) { + System.out.println("A Json Processing Exception Occurred while trying to get data from a mocked ssm."); + e.printStackTrace(); + } + return ssms; + } + +} + + + + diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockTimGenerator.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockTimGenerator.java new file mode 100644 index 00000000..d9a7dcf1 --- /dev/null +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockTimGenerator.java @@ -0,0 +1,39 @@ +package us.dot.its.jpo.ode.mockdata; + +import java.util.ArrayList; +import java.util.List; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + +import us.dot.its.jpo.ode.model.OdeTimData; +public class MockTimGenerator { + + static String timString = "{\"metadata\":{\"securityResultCode\":\"\",\"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\":\"02fd3137-f282-4f06-b735-66c5e2e963be\",\"bundleSize\":\"1\",\"bundleId\":\"0\"},\"logFileName\":\"\",\"odeReceivedAt\":\"2024-05-09T15:33:57.386279283Z\",\"originIp\":\"172.18.0.1\"},\"payload\":{\"data\":{\"MessageFrame\":{\"messageId\":\"31\",\"value\":{\"TravelerInformation\":{\"timeStamp\":\"449089\",\"packetID\":\"0000000000000BBC2B\",\"urlB\":\"null\",\"dataFrames\":{\"TravelerDataFrame\":{\"regions\":{\"GeographicalPath\":{\"closedPath\":{\"false\":\"\"},\"anchor\":{\"lat\":\"411269876\",\"long\":\"-1047269563\"},\"name\":\"westbound_I-80_366.0_365.0_RSU-10.145.1.100_RW_4456\",\"laneWidth\":\"32700\",\"directionality\":{\"both\":\"\"},\"description\":{\"path\":{\"offset\":{\"xy\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"node-LatLon\":{\"lon\":\"-1047287423\",\"lat\":\"411264686\"}}},{\"delta\":{\"node-LatLon\":{\"lon\":\"-1047305390\",\"lat\":\"411260104\"}}},{\"delta\":{\"node-LatLon\":{\"lon\":\"-1047323629\",\"lat\":\"411256185\"}}},{\"delta\":{\"node-LatLon\":{\"lon\":\"-1047342080\",\"lat\":\"411252886\"}}},{\"delta\":{\"node-LatLon\":{\"lon\":\"-1047360706\",\"lat\":\"411250207\"}}},{\"delta\":{\"node-LatLon\":{\"lon\":\"-1047379480\",\"lat\":\"411248201\"}}},{\"delta\":{\"node-LatLon\":{\"lon\":\"-1047398354\",\"lat\":\"411246839\"}}},{\"delta\":{\"node-LatLon\":{\"lon\":\"-1047417290\",\"lat\":\"411246133\"}}},{\"delta\":{\"node-LatLon\":{\"lon\":\"-1047436246\",\"lat\":\"411245796\"}}},{\"delta\":{\"node-LatLon\":{\"lon\":\"-1047455202\",\"lat\":\"411245470\"}}},{\"delta\":{\"node-LatLon\":{\"lon\":\"-1047474159\",\"lat\":\"411245173\"}}}]}}},\"scale\":\"0\"}},\"id\":{\"id\":\"0\",\"region\":\"0\"},\"direction\":\"0000000000010000\"}},\"duratonTime\":\"1440\",\"sspMsgRights1\":\"1\",\"sspMsgRights2\":\"1\",\"startYear\":\"2018\",\"msgId\":{\"roadSignID\":{\"viewAngle\":\"1111111111111111\",\"mutcdCode\":{\"warning\":\"\"},\"position\":{\"lat\":\"411269876\",\"long\":\"-1047269563\"}}},\"priority\":\"5\",\"content\":{\"advisory\":{\"SEQUENCE\":[{\"item\":{\"itis\":\"777\"}},{\"item\":{\"itis\":\"13579\"}}]}},\"url\":\"null\",\"sspTimRights\":\"1\",\"sspLocationRights\":\"1\",\"frameType\":{\"advisory\":\"\"},\"startTime\":\"448260\"}},\"msgCnt\":\"1\"}}}},\"dataType\":\"TravelerInformation\"}}"; + + public static List getJsonTims(){ + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerModule(new JavaTimeModule()); + + ArrayList tims = new ArrayList<>(); + + try { + OdeTimData tim = objectMapper.readValue(timString, OdeTimData.class); + tims.add(tim); + } catch (JsonMappingException e) { + System.out.println("A Json Mapping Exception Occurred while trying to get data from mocked tim."); + e.printStackTrace(); + } catch (JsonProcessingException e) { + System.out.println("A Json Processing Exception Occurred while trying to get data from a mocked tim."); + e.printStackTrace(); + } + return tims; + } + +} + + + + \ No newline at end of file diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockUploadDataGenerator.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockUploadDataGenerator.java deleted file mode 100644 index bb9de865..00000000 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockUploadDataGenerator.java +++ /dev/null @@ -1,25 +0,0 @@ -package us.dot.its.jpo.ode.mockdata; - -import java.time.Instant; -import java.util.UUID; - -import us.dot.its.jpo.ode.api.models.UploadData; - -public class MockUploadDataGenerator { - - public static UploadData getUploadData(){ - - - UploadData data = new UploadData(); - - data.bsmList.add("0022e12d18466c65c1493800000e00e4616183e85a8f0100c000038081bc001480b8494c4c950cd8cde6e9651116579f22a424dd78fffff00761e4fd7eb7d07f7fff80005f11d1020214c1c0ffc7c016aff4017a0ff65403b0fd204c20ffccc04f8fe40c420ffe6404cefe60e9a10133408fcfde1438103ab4138f00e1eec1048ec160103e237410445c171104e26bc103dc4154305c2c84103b1c1c8f0a82f42103f34262d1123198103dac25fb12034ce10381c259f12038ca103574251b10e3b2210324c23ad0f23d8efffe0000209340d10000004264bf00"); - data.mapList.add("001283c138003000205e9c014d3eab092ca624b5518202dc3658042800000400023622c60ca009f66d48abfaf81388d8ad18070027d9b2ffcfe9804f13667b1ffd009ec2c76e3ffc82c4e0001004b00c5000000800066c4574101813ecd8b757fae027d9b30e6ff5604ec363561fe7809ec6cd69bfec813c4d8a617fc9027d9b2147008604fb163666000016250000802580228000001000096229e1309b51a6fe4204dd361cf1fe5009f6018e1000096020a00000080004d88a57f84027d9b3827002804ec36087600a009f62c289407282c310001c0440188800000006c46dbe02813ec5816d800710052200000001b11b6fad404fb16054a0000401c8800000006c47b3d24813ec5816d801b100c4200000000af890f12c580007e87100d4200000008af4c0f12c580077e7a2c0004000160002001cb028d000000800052c160bc40b5fffd8a9409d86bfebb5b40141457fef53b76c008b467014145800080002bffcbffc82c6a0001804b024d000000800036c2213c3b013ecd80096d64027d9affd8cdfc04f635ff7983bc09f66c0082aa2014280b1b80006012c0b3400000100004b02bcf0f6d7fe065d602788b0138eb900b1240001012c083400000080009b0c2af0b804fb15fe6de171afff6c63e04ec15fe1de670060e40002581ea8000004000135da6df0180a0a6adc2c00d0143cd51897fda028c8abb25001a0b0680008012c105400000200009aedbefae005053540ee003c0a326a9cf3fed8143c5667780010582c0004009608aa00000080004d76de7ee402829aba88ffdc050f354525fff80a322bcf23fa602c690000c04b0395000000200016bb4fbd4e01414d3215800802940ab108fff2030d2000110126200000001aee5103be050a15f6f1ffc8404d8800000006bb97c18e0142857dfa800010146200000001aee89099a050a15f8720000b05dd000000800046be3743b781428d80e1b00002879b00514b4404f63600827d8c09e22c000400015ffe6007016190000402582ce8000004000135ecee1de80a146c02e54758143cd8059ad3e027b1b00613dd004f102c360000804b055d000000200046bcc7c3c781428d80108c6e02829b002b2ece050a16019a4b29b00ab5c3604f136004e410409ec018a10000960c3a00000080004d7de9878602851b003923cc05053601623b440a0a6bfb8c3a5014140b0640005012c197400000100005afe570ef2050a36003a47c80a0a6bfd2c45f014140b054000501101a8200000001b05a90edc050535ffe605800a0a101b8200000001b08a30ec0050535ffe605300a0a101c8200000005b0c6f0ea4050515ffca0568b0001000e"); - data.spatList.add("001338000817a780000089680500204642b342b34802021a15a955a940181190acd0acd20100868555c555c00104342aae2aae002821a155715570"); - - data.ID = UUID.randomUUID().toString().replace("-", ""); - data.uploadTime = Instant.now().toEpochMilli(); - - return data; - } - -} diff --git a/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/MapTest.java b/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/MapTest.java index bf789b1b..420faa94 100644 --- a/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/MapTest.java +++ b/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/MapTest.java @@ -16,6 +16,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.test.context.junit4.SpringRunner; +import us.dot.its.jpo.geojsonconverter.pojos.geojson.LineString; import us.dot.its.jpo.geojsonconverter.pojos.geojson.map.ProcessedMap; import us.dot.its.jpo.ode.api.accessors.map.ProcessedMapRepository; import us.dot.its.jpo.ode.api.controllers.MapController; @@ -40,12 +41,12 @@ public void testProcessedMap() { MockKeyCloakAuth.setSecurityContextHolder("cm_user", Set.of("USER")); - List list = MockMapGenerator.getProcessedMaps(); + List> list = MockMapGenerator.getProcessedMaps(); Query query = processedMapRepo.getQuery(null, null, null, false); when(processedMapRepo.findProcessedMaps(query)).thenReturn(list); - ResponseEntity> result = controller.findMaps(null, null, null, false, false); + ResponseEntity>> result = controller.findMaps(null, null, null, false, false); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(result.getBody()).isEqualTo(list); } diff --git a/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/accessorTests/map/ProcessedMapRepositoryImplTest.java b/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/accessorTests/map/ProcessedMapRepositoryImplTest.java index 5c501607..05a66e8d 100644 --- a/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/accessorTests/map/ProcessedMapRepositoryImplTest.java +++ b/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/accessorTests/map/ProcessedMapRepositoryImplTest.java @@ -18,6 +18,7 @@ import org.bson.Document; +import us.dot.its.jpo.geojsonconverter.pojos.geojson.LineString; import us.dot.its.jpo.geojsonconverter.pojos.geojson.map.ProcessedMap; import us.dot.its.jpo.ode.api.accessors.map.ProcessedMapRepositoryImpl; import us.dot.its.jpo.ode.api.models.IDCount; @@ -86,11 +87,11 @@ public void testGetQueryResultCount() { @Test public void testFindProcessedMaps() { Query query = new Query(); - List expectedMaps = new ArrayList<>(); + List> expectedMaps = new ArrayList<>(); Mockito.doReturn(expectedMaps).when(mongoTemplate).find(query, ProcessedMap.class, "ProcessedMap"); - List resultMaps = repository.findProcessedMaps(query); + List> resultMaps = repository.findProcessedMaps(query); assertThat(resultMaps).isEqualTo(expectedMaps); } From 838bf36ac46a1aba449ce7d7f1219ab423d60eba Mon Sep 17 00:00:00 2001 From: John-Wiens Date: Thu, 9 May 2024 17:39:28 -0600 Subject: [PATCH 09/29] Added decoder logic for various message types --- .../jpo/ode/api/ConflictApiApplication.java | 10 +- .../dot/its/jpo/ode/api/asn1/BsmDecoder.java | 125 +++++++ .../us/dot/its/jpo/ode/api/asn1/Decoder.java | 316 +----------------- .../its/jpo/ode/api/asn1/DecoderManager.java | 231 +++++++++++++ .../dot/its/jpo/ode/api/asn1/MapDecoder.java | 124 +++++++ .../dot/its/jpo/ode/api/asn1/SpatDecoder.java | 135 ++++++++ .../dot/its/jpo/ode/api/asn1/SrmDecoder.java | 119 +++++++ .../dot/its/jpo/ode/api/asn1/SsmDecoder.java | 117 +++++++ .../dot/its/jpo/ode/api/asn1/TimDecoder.java | 117 +++++++ .../api/controllers/DecoderController.java | 158 +++++---- .../models/messages/BsmDecodedMessage.java | 4 +- .../api/models/messages/EncodedMessage.java | 4 + .../models/messages/MapDecodedMessage.java | 4 +- .../models/messages/SpatDecodedMessage.java | 4 +- .../models/messages/SrmDecodedMessage.java | 4 +- .../models/messages/SsmDecodedMessage.java | 4 +- .../models/messages/TimDecodedMessage.java | 4 +- .../mockdata/MockDecodedMessageGenerator.java | 13 +- 18 files changed, 1090 insertions(+), 403 deletions(-) create mode 100644 api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/BsmDecoder.java create mode 100644 api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/DecoderManager.java create mode 100644 api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/MapDecoder.java create mode 100644 api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/SpatDecoder.java create mode 100644 api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/SrmDecoder.java create mode 100644 api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/SsmDecoder.java create mode 100644 api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/TimDecoder.java diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/ConflictApiApplication.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/ConflictApiApplication.java index 530b8189..3cd26972 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/ConflictApiApplication.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/ConflictApiApplication.java @@ -5,10 +5,10 @@ import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.web.servlet.config.annotation.EnableWebMvc; -import us.dot.its.jpo.ode.api.asn1.Decoder; +import us.dot.its.jpo.ode.api.asn1.DecoderManager; +import us.dot.its.jpo.ode.api.models.MessageType; import us.dot.its.jpo.ode.api.models.messages.BsmDecodedMessage; -import us.dot.its.jpo.ode.api.models.messages.DecodedMessage; -import us.dot.its.jpo.ode.model.OdeBsmData; +import us.dot.its.jpo.ode.api.models.messages.EncodedMessage; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; import org.springframework.scheduling.annotation.EnableScheduling; @@ -28,11 +28,11 @@ public static void main(String[] args) { System.out.println("Conflict Monitor API docs page found here: http://localhost:8081/swagger-ui/index.html"); System.out.println("Startup Complete"); - BsmDecodedMessage bsm = (BsmDecodedMessage)Decoder.decode("00145144ad0b7947c2ed9ad2748035a4e8ff880000000fd2229199307d7d07d0b17fff05407d12720038c000fe72c107b001ea88fffeb4002127c0009000000fdfffe3ffff9407344704000041910120100000000efc10609c26e900e11f61a947802127c0009000000fdfffe3ffff9407453304000041910120100000008ffffe501ca508100000000000a508100000404804000000849f00024000003f7fff8ffffe501ca508100000fe501ca508100000fffe501ca51c10000000024000003f7fff8ffffe501ca51c1"); + // BsmDecodedMessage bsm = (BsmDecodedMessage)DecoderManager.decode(new EncodedMessage("00145144ad0b7947c2ed9ad2748035a4e8ff880000000fd2229199307d7d07d0b17fff05407d12720038c000fe72c107b001ea88fffeb4002127c0009000000fdfffe3ffff9407344704000041910120100000000efc10609c26e900e11f61a947802127c0009000000fdfffe3ffff9407453304000041910120100000008ffffe501ca508100000000000a508100000404804000000849f00024000003f7fff8ffffe501ca508100000fe501ca508100000fffe501ca51c10000000024000003f7fff8ffffe501ca51c1", MessageType.BSM)); - System.out.println("Fully Decoded BSM" + bsm.getBsm()); + // System.out.println("Fully Decoded BSM" + bsm.getBsm()); // OdeBsmData decodeTest; // try { // decodeTest = OdeBsmDataCreatorHelper.createOdeBsmData("RVbsmTxsuccessRVunsecuredDataMessageFrameUPERus.dot.its.jpo.ode.model.OdeAsn1Payloadf48cd9fa-cf32-4542-9cfd-4ff0542d56b010002024-05-07T18:45:29.796137Z60false10.165.5.230MessageFrame201052123484129609403192909-111704926414321404081005421320812720012001-12700111132012800-14434616781268-3227713981122522407912025454581096553565535101051"); diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/BsmDecoder.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/BsmDecoder.java new file mode 100644 index 00000000..7e176979 --- /dev/null +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/BsmDecoder.java @@ -0,0 +1,125 @@ +package us.dot.its.jpo.ode.api.asn1; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import us.dot.its.jpo.ode.api.models.messages.BsmDecodedMessage; +import us.dot.its.jpo.ode.api.models.messages.DecodedMessage; +import us.dot.its.jpo.ode.api.models.messages.EncodedMessage; +import us.dot.its.jpo.ode.context.AppContext; +import us.dot.its.jpo.ode.model.Asn1Encoding; +import us.dot.its.jpo.ode.model.Asn1Encoding.EncodingRule; +import us.dot.its.jpo.ode.model.OdeAsn1Data; +import us.dot.its.jpo.ode.model.OdeAsn1Payload; +import us.dot.its.jpo.ode.model.OdeBsmData; +import us.dot.its.jpo.ode.model.OdeBsmMetadata; +import us.dot.its.jpo.ode.model.OdeBsmMetadata.BsmSource; +import us.dot.its.jpo.ode.model.OdeBsmPayload; +import us.dot.its.jpo.ode.model.OdeData; +import us.dot.its.jpo.ode.model.OdeHexByteArray; +import us.dot.its.jpo.ode.model.OdeLogMetadata.RecordType; +import us.dot.its.jpo.ode.model.OdeLogMetadata.SecurityResultCode; +import us.dot.its.jpo.ode.model.OdeLogMsgMetadataLocation; +import us.dot.its.jpo.ode.model.OdeMsgPayload; +import us.dot.its.jpo.ode.model.ReceivedMessageDetails; +import us.dot.its.jpo.ode.model.RxSource; +import us.dot.its.jpo.ode.plugin.j2735.builders.BsmBuilder; +import us.dot.its.jpo.ode.util.JsonUtils; +import us.dot.its.jpo.ode.util.XmlUtils; +import us.dot.its.jpo.ode.util.XmlUtils.XmlUtilsException; + +public class BsmDecoder implements Decoder { + + + @Override + public DecodedMessage decode(EncodedMessage message) { + + // Convert to Ode Data type and Add Metadata + OdeData data = getAsOdeData(message.getAsn1Message()); + + XmlUtils xmlUtils = new XmlUtils(); + + try { + // Convert to XML for ASN.1 Decoder + String xml = xmlUtils.toXml(data); + + // Send String through ASN.1 Decoder to get Decoded XML Data + String decodedXml = DecoderManager.decodeXmlWithAcm(xml); + + + // Convert to Ode Json + OdeBsmData bsm = getAsOdeJson(decodedXml); + + // build output data structure + DecodedMessage decodedMessage = new BsmDecodedMessage(bsm, message.getAsn1Message(), ""); + return decodedMessage; + + } catch (JsonProcessingException e) { + e.printStackTrace(); + return new BsmDecodedMessage(null, message.getAsn1Message(), e.getMessage()); + } catch (Exception e) { + e.printStackTrace(); + return new BsmDecodedMessage(null, message.getAsn1Message(), e.getMessage()); + } + } + + @Override + public OdeData getAsOdeData(String encodedData) { + OdeMsgPayload payload = new OdeAsn1Payload(new OdeHexByteArray(encodedData)); + + // construct metadata + OdeBsmMetadata metadata = new OdeBsmMetadata(payload); + metadata.setOdeReceivedAt(DecoderManager.getOdeReceivedAt()); + metadata.setRecordType(RecordType.bsmTx); + metadata.setSecurityResultCode(SecurityResultCode.success); + + // construct metadata: receivedMessageDetails + ReceivedMessageDetails receivedMessageDetails = new ReceivedMessageDetails(); + receivedMessageDetails.setRxSource(RxSource.RV); + + // construct metadata: locationData + OdeLogMsgMetadataLocation locationData = new OdeLogMsgMetadataLocation(); + receivedMessageDetails.setLocationData(locationData); + + metadata.setReceivedMessageDetails(receivedMessageDetails); + metadata.setBsmSource(BsmSource.RV); + + Asn1Encoding unsecuredDataEncoding = new Asn1Encoding("unsecuredData", "MessageFrame", + EncodingRule.UPER); + metadata.addEncoding(unsecuredDataEncoding); + + // construct odeData + return new OdeAsn1Data(metadata, payload); + } + + @Override + public OdeBsmData getAsOdeJson(String consumedData) throws XmlUtilsException { + ObjectNode consumed = XmlUtils.toObjectNode(consumedData); + + JsonNode metadataNode = consumed.findValue(AppContext.METADATA_STRING); + if (metadataNode instanceof ObjectNode) { + ObjectNode object = (ObjectNode) metadataNode; + object.remove(AppContext.ENCODINGS_STRING); + } + + OdeBsmMetadata metadata = (OdeBsmMetadata) JsonUtils.fromJson( + metadataNode.toString(), OdeBsmMetadata.class); + + /* + * ODE-755 and ODE-765 Starting with schemaVersion=5 receivedMessageDetails + * will be present in BSM metadata. None should be present in prior versions. + */ + if (metadata.getSchemaVersion() <= 4) { + metadata.setReceivedMessageDetails(null); + } + + OdeBsmPayload payload = new OdeBsmPayload( + BsmBuilder.genericBsm(consumed.findValue("BasicSafetyMessage"))); + return new OdeBsmData(metadata, payload); + } + + public String mockDecodeXmlWithAcm(String xmlMessage){ + return "RVbsmTxsuccessRVunsecuredDataMessageFrameUPERus.dot.its.jpo.ode.model.OdeAsn1Payload8829c539-e684-40b7-a786-692acd3f897a10002024-05-08T20:47:53.830130272Z60falseMessageFrame2018B42DE51F299800000810013156448-100500000002505900-79521090627893276718020065535655351010527101001000000000000000"; + } + +} diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/Decoder.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/Decoder.java index 132e6e5b..0d1c7059 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/Decoder.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/Decoder.java @@ -1,318 +1,14 @@ package us.dot.its.jpo.ode.api.asn1; -import lombok.extern.slf4j.Slf4j; -import us.dot.its.jpo.ode.api.models.MessageType; -import us.dot.its.jpo.ode.api.models.TypePayload; -import us.dot.its.jpo.ode.api.models.messages.BsmDecodedMessage; import us.dot.its.jpo.ode.api.models.messages.DecodedMessage; -import us.dot.its.jpo.ode.model.Asn1Encoding; -import us.dot.its.jpo.ode.model.Asn1Encoding.EncodingRule; -import us.dot.its.jpo.ode.model.OdeAsn1Data; -import us.dot.its.jpo.ode.model.OdeAsn1Payload; -import us.dot.its.jpo.ode.model.OdeBsmData; -import us.dot.its.jpo.ode.model.OdeBsmMetadata; -import us.dot.its.jpo.ode.model.OdeBsmMetadata.BsmSource; -import us.dot.its.jpo.ode.model.OdeBsmPayload; +import us.dot.its.jpo.ode.api.models.messages.EncodedMessage; import us.dot.its.jpo.ode.model.OdeData; -import us.dot.its.jpo.ode.model.OdeHexByteArray; -import us.dot.its.jpo.ode.model.OdeLogMetadata.RecordType; -import us.dot.its.jpo.ode.model.OdeLogMetadata.SecurityResultCode; -import us.dot.its.jpo.ode.model.OdeLogMsgMetadataLocation; -import us.dot.its.jpo.ode.model.OdeMsgPayload; -import us.dot.its.jpo.ode.model.ReceivedMessageDetails; -import us.dot.its.jpo.ode.model.RxSource; -import us.dot.its.jpo.ode.plugin.j2735.builders.BsmBuilder; -import us.dot.its.jpo.ode.util.JsonUtils; -import us.dot.its.jpo.ode.util.XmlUtils; import us.dot.its.jpo.ode.util.XmlUtils.XmlUtilsException; -import us.dot.its.jpo.ode.context.AppContext; - -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.ArrayUtils; -import org.springframework.stereotype.Component; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; - -import java.io.*; -import java.nio.charset.StandardCharsets; -import java.nio.file.Path; -import java.time.ZoneOffset; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.util.UUID; - -// import static us.dot.its.jpo.ode.api.asn1.extractMessageFrame; - - - -@Component -@Slf4j -public class Decoder { - - public static final MessageType[] types = {MessageType.BSM, MessageType.MAP, MessageType.SPAT, MessageType.SRM, MessageType.SSM, MessageType.TIM}; - public static final String[] startFlags = {"0014", "0012", "0013", "001d", "001e", "001f"}; //BSM, MAP, SPAT, SRM, SSM, TIM - public static final int[] maxSizes = {500, 2048, 1000, 500, 500, 500}; - public static final int HEADER_MINIMUM_SIZE = 20; - public static final int bufferSize = 2048; - - public static DecodedMessage decode(String inputAsn1){ - - // Identify Message Type and Cut off any extra characters - TypePayload payload = identifyAsn1(inputAsn1); - - - // Convert Payload to Pojo and add Metadata - OdeData data = getAsOdeData(payload); - - XmlUtils xmlUtils = new XmlUtils(); - - try { - // Convert to XML for ASN.1 Decoder - String xml = xmlUtils.toXml(data); - System.out.println("XML" + xml); - - // Send String through ASN.1 Decoder to get Decoded XML Data - // String decodedXml = decodeXmlWithAcm(xml); - String decodedXml = mockDecodeXmlWithAcm(xml); - - if(payload.getType().equals(MessageType.BSM)){ - OdeBsmData bsm = createOdeBsmData(decodedXml); - DecodedMessage message = new BsmDecodedMessage(bsm, inputAsn1, MessageType.BSM, ""); - return message; - } - - } catch (JsonProcessingException e) { - e.printStackTrace(); - } catch (Exception e) { - e.printStackTrace(); - } - - - - return null; - } - - public static DecodedMessage decodeAsType(String inputAsn1, MessageType type){ - String payload = removeHeader(inputAsn1, type); - - if(payload != null){ - - } - - return null; - - } - - public static String getOdeReceivedAt(){ - ZonedDateTime utc = ZonedDateTime.now(ZoneOffset.UTC); - String timestamp = utc.format(DateTimeFormatter.ISO_INSTANT); - return timestamp; - } - - public static OdeData getAsOdeData(TypePayload payload){ - - if(payload.getType() == MessageType.BSM){ - return getBsmAsOdeData(payload.getPayload()); - } - - return null; - - } - - public static OdeData getBsmAsOdeData(String encodedPayload){ - OdeMsgPayload payload = new OdeAsn1Payload(new OdeHexByteArray(encodedPayload)); - - // construct metadata - OdeBsmMetadata metadata = new OdeBsmMetadata(payload); - metadata.setOdeReceivedAt(getOdeReceivedAt()); - metadata.setRecordType(RecordType.bsmTx); - metadata.setSecurityResultCode(SecurityResultCode.success); - - // construct metadata: receivedMessageDetails - ReceivedMessageDetails receivedMessageDetails = new ReceivedMessageDetails(); - receivedMessageDetails.setRxSource(RxSource.RV); - - // construct metadata: locationData - OdeLogMsgMetadataLocation locationData = new OdeLogMsgMetadataLocation(); - receivedMessageDetails.setLocationData(locationData); - - metadata.setReceivedMessageDetails(receivedMessageDetails); - metadata.setBsmSource(BsmSource.RV); - - Asn1Encoding unsecuredDataEncoding = new Asn1Encoding("unsecuredData", "MessageFrame", - EncodingRule.UPER); - metadata.addEncoding(unsecuredDataEncoding); - - // construct odeData - return new OdeAsn1Data(metadata, payload); - } - - public static OdeBsmData createOdeBsmData(String consumedData) throws XmlUtilsException { - ObjectNode consumed = XmlUtils.toObjectNode(consumedData); - - JsonNode metadataNode = consumed.findValue(AppContext.METADATA_STRING); - if (metadataNode instanceof ObjectNode) { - ObjectNode object = (ObjectNode) metadataNode; - object.remove(AppContext.ENCODINGS_STRING); - } - - OdeBsmMetadata metadata = (OdeBsmMetadata) JsonUtils.fromJson( - metadataNode.toString(), OdeBsmMetadata.class); - - /* - * ODE-755 and ODE-765 Starting with schemaVersion=5 receivedMessageDetails - * will be present in BSM metadata. None should be present in prior versions. - */ - if (metadata.getSchemaVersion() <= 4) { - metadata.setReceivedMessageDetails(null); - } - - OdeBsmPayload payload = new OdeBsmPayload( - BsmBuilder.genericBsm(consumed.findValue("BasicSafetyMessage"))); - return new OdeBsmData(metadata, payload); - } +public interface Decoder { + public DecodedMessage decode(EncodedMessage message); + public OdeData getAsOdeData(String encodedData); + public OdeData getAsOdeJson(String encodedXml) throws XmlUtilsException; - - - - - public static String removeHeader(String hexPacket, MessageType type) { - - // logger.debug("BSM packet length: {}, start index: {}", - // hexPacket.length(), startIndex); - - String startFlag = startFlags[ArrayUtils.indexOf(types, type)]; - - int startIndex = hexPacket.indexOf(startFlag); - if (startIndex == 0) { - // Raw Message no Headers - } else if (startIndex == -1) { - - return null; - } else { - // We likely found a message with a header, look past the first 20 - // bytes for the start of the BSM - int trueStartIndex = HEADER_MINIMUM_SIZE - + hexPacket.substring(HEADER_MINIMUM_SIZE, hexPacket.length()).indexOf(startFlag); - hexPacket = hexPacket.substring(trueStartIndex, hexPacket.length()); - } - - return hexPacket; - } - - - public static TypePayload identifyAsn1(String hexPacket){ - - - // Compute the Effective End Location of the real data. - //int endIndex = hexPacket.indexOf("0000000000000000"); - //if(endIndex == -1){ - int endIndex = hexPacket.length()-1; - //} - - int closestStartIndex = endIndex; - MessageType closestMessageType = MessageType.UNKNOWN; - int closestBufferSize = bufferSize; - - - for(int i = 0; i< startFlags.length; i++){ - - String startFlag = startFlags[i]; - MessageType mType = types[i]; - int typeBufferSize = maxSizes[i]; - - - // Skip possible message type if packet is too big - if(endIndex > typeBufferSize*2){ - continue; - } - - - int startIndex = hexPacket.indexOf(startFlag); - - if (startIndex == 0) { - return new TypePayload(mType, hexPacket); - }else if (startIndex == -1) { - continue; - } else{ - int trueStartIndex = hexPacket.substring(HEADER_MINIMUM_SIZE, hexPacket.length()).indexOf(startFlag); - if(trueStartIndex ==-1){ - continue; - } - trueStartIndex += HEADER_MINIMUM_SIZE; - - while (trueStartIndex != -1 && (trueStartIndex % 2 == 1) && trueStartIndex < hexPacket.length()-4){ - int newStartIndex = hexPacket.substring(trueStartIndex+1, hexPacket.length()).indexOf(startFlag); - if(newStartIndex == -1){ - trueStartIndex = -1; - break; - }else{ - trueStartIndex += newStartIndex+1; - } - } - - if(trueStartIndex != -1 && trueStartIndex < closestStartIndex){ - closestStartIndex = trueStartIndex; - closestMessageType = mType; - closestBufferSize = typeBufferSize; - } - } - } - - if(closestMessageType == MessageType.UNKNOWN){ - return new TypePayload(MessageType.UNKNOWN, hexPacket); - }else{ - return new TypePayload(closestMessageType, hexPacket.substring(closestStartIndex, hexPacket.length())); - } - } - - - public static String mockDecodeXmlWithAcm(String xmlMessage){ - return "RVbsmTxsuccessRVunsecuredDataMessageFrameUPERus.dot.its.jpo.ode.model.OdeAsn1Payload8829c539-e684-40b7-a786-692acd3f897a10002024-05-08T20:47:53.830130272Z60falseMessageFrame2018B42DE51F299800000810013156448-100500000002505900-79521090627893276718020065535655351010527101001000000000000000"; - } - - - public static String decodeXmlWithAcm(String xmlMessage) throws Exception { - - - System.out.println("Decoding Message: " + xmlMessage); - log.info("Decoding message: {}", xmlMessage); - - // Save XML to temp file - String tempDir = FileUtils.getTempDirectoryPath(); - String tempFileName = "asn1-codec-java-" + UUID.randomUUID().toString() + ".xml"; - log.info("Temp file name: {}", tempFileName); - System.out.println("Temp File Name: " + tempFileName); - Path tempFilePath = Path.of(tempDir, tempFileName); - File tempFile = new File(tempFilePath.toString()); - FileUtils.writeStringToFile(tempFile, xmlMessage, StandardCharsets.UTF_8); - - // Run ACM tool to decode message - var pb = new ProcessBuilder( - "/build/acm", "-F", "-c", "/build/config/example.properties", "-T", "decode", tempFile.getAbsolutePath()); - pb.directory(new File("/build")); - Process process = pb.start(); - String result = IOUtils.toString(process.getInputStream(), StandardCharsets.UTF_8); - log.info("Result: {}", result); - System.out.println("Decode Result: " + result); - - // Clean up temp file - // tempFile.delete(); - - return result; - - // // Remove wrapping from result to just return the XER - // try { - // String messageFrame = extractMessageFrame(result); - // log.info("Message frame: {}", messageFrame); - // return messageFrame; - // } catch (Exception e) { - // log.error("Error extracting message frame: {}, returning result which is probably an error message", e); - // return result; - // } - } -} \ No newline at end of file +} diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/DecoderManager.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/DecoderManager.java new file mode 100644 index 00000000..4b6af9b7 --- /dev/null +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/DecoderManager.java @@ -0,0 +1,231 @@ +package us.dot.its.jpo.ode.api.asn1; + +import lombok.extern.slf4j.Slf4j; +import us.dot.its.jpo.ode.api.models.MessageType; +import us.dot.its.jpo.ode.api.models.messages.DecodedMessage; +import us.dot.its.jpo.ode.api.models.messages.EncodedMessage; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.ArrayUtils; +import org.springframework.stereotype.Component; +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.UUID; + + +@Component +@Slf4j +public class DecoderManager { + + public static final MessageType[] types = {MessageType.BSM, MessageType.MAP, MessageType.SPAT, MessageType.SRM, MessageType.SSM, MessageType.TIM}; + public static final String[] startFlags = {"0014", "0012", "0013", "001d", "001e", "001f"}; //BSM, MAP, SPAT, SRM, SSM, TIM + public static final int[] maxSizes = {500, 2048, 1000, 500, 500, 500}; + public static final int HEADER_MINIMUM_SIZE = 20; + public static final int bufferSize = 2048; + + // public static DecodedMessage decode(String inputAsn1){ + + // // Identify Message Type and Cut off any extra characters + // TypePayload payload = identifyAsn1(inputAsn1); + + + // // Convert Payload to Pojo and add Metadata + // OdeData data = getAsOdeData(payload); + + // XmlUtils xmlUtils = new XmlUtils(); + + // try { + // // Convert to XML for ASN.1 Decoder + // String xml = xmlUtils.toXml(data); + // System.out.println("XML" + xml); + + // // Send String through ASN.1 Decoder to get Decoded XML Data + // // String decodedXml = decodeXmlWithAcm(xml); + // String decodedXml = mockDecodeXmlWithAcm(xml); + + // if(payload.getType().equals(MessageType.BSM)){ + // OdeBsmData bsm = createOdeBsmData(decodedXml); + // DecodedMessage message = new BsmDecodedMessage(bsm, inputAsn1, MessageType.BSM, ""); + // return message; + // } + + // } catch (JsonProcessingException e) { + // e.printStackTrace(); + // } catch (Exception e) { + // e.printStackTrace(); + // } + + + + // return null; + // } + + public static DecodedMessage decode(EncodedMessage message){ + String payload = removeHeader(message.getAsn1Message(), message.getType()); + message.setAsn1Message(payload); + + Decoder decoder = null; + + if(payload != null){ + if(message.getType() == MessageType.BSM){ + decoder = new BsmDecoder(); + } + else if(message.getType() == MessageType.MAP){ + + }else if(message.getType() == MessageType.SPAT){ + + }else if(message.getType() == MessageType.SRM){ + + }else if(message.getType() == MessageType.SSM){ + + }else if(message.getType() == MessageType.TIM){ + + }else{ + + } + + if(decoder!= null){ + return decoder.decode(message); + }else{ + return new DecodedMessage(payload, message.getType(), "No Valid Decoder found for Message Type"); + } + + } + + return new DecodedMessage(payload, message.getType(), "Unable to find valid message start flag within input data"); + + } + + public static String getOdeReceivedAt(){ + ZonedDateTime utc = ZonedDateTime.now(ZoneOffset.UTC); + String timestamp = utc.format(DateTimeFormatter.ISO_INSTANT); + return timestamp; + } + + public static String getOriginIp(){ + return "user-upload"; + } + + public static String removeHeader(String hexPacket, MessageType type) { + + String startFlag = startFlags[ArrayUtils.indexOf(types, type)]; + + int startIndex = hexPacket.indexOf(startFlag); + if (startIndex == 0) { + // Raw Message no Headers + } else if (startIndex == -1) { + + return null; + } else { + // We likely found a message with a header, look past the first 20 + // bytes for the start of the BSM + int trueStartIndex = HEADER_MINIMUM_SIZE + + hexPacket.substring(HEADER_MINIMUM_SIZE, hexPacket.length()).indexOf(startFlag); + hexPacket = hexPacket.substring(trueStartIndex, hexPacket.length()); + } + + return hexPacket; + } + + + public static EncodedMessage identifyAsn1(String hexPacket){ + // Compute the Effective End Location of the real data. + //int endIndex = hexPacket.indexOf("0000000000000000"); + //if(endIndex == -1){ + int endIndex = hexPacket.length()-1; + //} + + int closestStartIndex = endIndex; + MessageType closestMessageType = MessageType.UNKNOWN; + // int closestBufferSize = bufferSize; + + + for(int i = 0; i< startFlags.length; i++){ + + String startFlag = startFlags[i]; + MessageType mType = types[i]; + int typeBufferSize = maxSizes[i]; + + + // Skip possible message type if packet is too big + if(endIndex > typeBufferSize*2){ + continue; + } + + + int startIndex = hexPacket.indexOf(startFlag); + + if (startIndex == 0) { + return new EncodedMessage(hexPacket, mType); + }else if (startIndex == -1) { + continue; + } else{ + int trueStartIndex = hexPacket.substring(HEADER_MINIMUM_SIZE, hexPacket.length()).indexOf(startFlag); + if(trueStartIndex ==-1){ + continue; + } + trueStartIndex += HEADER_MINIMUM_SIZE; + + while (trueStartIndex != -1 && (trueStartIndex % 2 == 1) && trueStartIndex < hexPacket.length()-4){ + int newStartIndex = hexPacket.substring(trueStartIndex+1, hexPacket.length()).indexOf(startFlag); + if(newStartIndex == -1){ + trueStartIndex = -1; + break; + }else{ + trueStartIndex += newStartIndex+1; + } + } + + if(trueStartIndex != -1 && trueStartIndex < closestStartIndex){ + closestStartIndex = trueStartIndex; + closestMessageType = mType; + // closestBufferSize = typeBufferSize; + } + } + } + + if(closestMessageType == MessageType.UNKNOWN){ + return new EncodedMessage(hexPacket, MessageType.UNKNOWN); + }else{ + return new EncodedMessage(hexPacket.substring(closestStartIndex, hexPacket.length()), closestMessageType); + } + } + + + + + + public static String decodeXmlWithAcm(String xmlMessage) throws Exception { + + + System.out.println("Decoding Message: " + xmlMessage); + log.info("Decoding message: {}", xmlMessage); + + // Save XML to temp file + String tempDir = FileUtils.getTempDirectoryPath(); + String tempFileName = "asn1-codec-java-" + UUID.randomUUID().toString() + ".xml"; + log.info("Temp file name: {}", tempFileName); + System.out.println("Temp File Name: " + tempFileName); + Path tempFilePath = Path.of(tempDir, tempFileName); + File tempFile = new File(tempFilePath.toString()); + FileUtils.writeStringToFile(tempFile, xmlMessage, StandardCharsets.UTF_8); + + // Run ACM tool to decode message + var pb = new ProcessBuilder( + "/build/acm", "-F", "-c", "/build/config/example.properties", "-T", "decode", tempFile.getAbsolutePath()); + pb.directory(new File("/build")); + Process process = pb.start(); + String result = IOUtils.toString(process.getInputStream(), StandardCharsets.UTF_8); + log.info("Result: {}", result); + System.out.println("Decode Result: " + result); + + // Clean up temp file + tempFile.delete(); + + return result; + } +} \ No newline at end of file diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/MapDecoder.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/MapDecoder.java new file mode 100644 index 00000000..e17ff29a --- /dev/null +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/MapDecoder.java @@ -0,0 +1,124 @@ +package us.dot.its.jpo.ode.api.asn1; + + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import us.dot.its.jpo.ode.api.models.messages.MapDecodedMessage; +import us.dot.its.jpo.ode.api.models.messages.DecodedMessage; +import us.dot.its.jpo.ode.api.models.messages.EncodedMessage; +import us.dot.its.jpo.ode.context.AppContext; +import us.dot.its.jpo.ode.model.Asn1Encoding; +import us.dot.its.jpo.ode.model.Asn1Encoding.EncodingRule; +import us.dot.its.jpo.ode.model.OdeAsn1Data; +import us.dot.its.jpo.ode.model.OdeAsn1Payload; +import us.dot.its.jpo.ode.model.OdeMapData; +import us.dot.its.jpo.ode.model.OdeMapMetadata; +import us.dot.its.jpo.ode.model.OdeMapMetadata.MapSource; +import us.dot.its.jpo.ode.model.OdeMapPayload; +import us.dot.its.jpo.ode.model.OdeData; +import us.dot.its.jpo.ode.model.OdeHexByteArray; +import us.dot.its.jpo.ode.model.OdeLogMetadata.RecordType; +import us.dot.its.jpo.ode.model.OdeLogMetadata.SecurityResultCode; +import us.dot.its.jpo.ode.model.OdeMsgPayload; +import us.dot.its.jpo.ode.model.ReceivedMessageDetails; +import us.dot.its.jpo.ode.model.RxSource; +import us.dot.its.jpo.ode.plugin.j2735.builders.MAPBuilder; +import us.dot.its.jpo.ode.util.JsonUtils; +import us.dot.its.jpo.ode.util.XmlUtils; +import us.dot.its.jpo.ode.util.XmlUtils.XmlUtilsException; + +public class MapDecoder implements Decoder { + + + @Override + public DecodedMessage decode(EncodedMessage message) { + + // Convert to Ode Data type and Add Metadata + OdeData data = getAsOdeData(message.getAsn1Message()); + + XmlUtils xmlUtils = new XmlUtils(); + + try { + // Convert to XML for ASN.1 Decoder + String xml = xmlUtils.toXml(data); + + // Send String through ASN.1 Decoder to get Decoded XML Data + String decodedXml = DecoderManager.decodeXmlWithAcm(xml); + + + // Convert to Ode Json + OdeMapData map = getAsOdeJson(decodedXml); + + // build output data structure + DecodedMessage decodedMessage = new MapDecodedMessage(null, map, message.getAsn1Message(), ""); + return decodedMessage; + + } catch (JsonProcessingException e) { + e.printStackTrace(); + return new MapDecodedMessage(null, null, message.getAsn1Message(), e.getMessage()); + } catch (Exception e) { + e.printStackTrace(); + return new MapDecodedMessage(null, null, message.getAsn1Message(), e.getMessage()); + } + } + + @Override + public OdeData getAsOdeData(String encodedData) { + OdeMsgPayload payload = new OdeAsn1Payload(new OdeHexByteArray(encodedData)); + + // construct metadata + OdeMapMetadata metadata = new OdeMapMetadata(payload); + + metadata.setOdeReceivedAt(DecoderManager.getOdeReceivedAt()); + metadata.setOriginIp(DecoderManager.getOriginIp()); + metadata.setRecordType(RecordType.mapTx); + metadata.setSecurityResultCode(SecurityResultCode.success); + + + metadata.setMapSource(MapSource.RSU); + + + Asn1Encoding unsecuredDataEncoding = new Asn1Encoding("unsecuredData", "MessageFrame", + EncodingRule.UPER); + metadata.addEncoding(unsecuredDataEncoding); + + // construct odeData + return new OdeAsn1Data(metadata, payload); + } + + @Override + public OdeMapData getAsOdeJson(String consumedData) throws XmlUtilsException { + ObjectNode consumed = XmlUtils.toObjectNode(consumedData); + + JsonNode metadataNode = consumed.findValue(AppContext.METADATA_STRING); + if (metadataNode instanceof ObjectNode) { + ObjectNode object = (ObjectNode) metadataNode; + object.remove(AppContext.ENCODINGS_STRING); + + // Map header file does not have a location and use predefined set required + // RxSource + ReceivedMessageDetails receivedMessageDetails = new ReceivedMessageDetails(); + receivedMessageDetails.setRxSource(RxSource.NA); + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode jsonNode; + try { + jsonNode = objectMapper.readTree(receivedMessageDetails.toJson()); + object.set(AppContext.RECEIVEDMSGDETAILS_STRING, jsonNode); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + } + + OdeMapMetadata metadata = (OdeMapMetadata) JsonUtils.fromJson(metadataNode.toString(), OdeMapMetadata.class); + + if (metadata.getSchemaVersion() <= 4) { + metadata.setReceivedMessageDetails(null); + } + + OdeMapPayload payload = new OdeMapPayload(MAPBuilder.genericMAP(consumed.findValue("MapData"))); + return new OdeMapData(metadata, payload); + } + +} diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/SpatDecoder.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/SpatDecoder.java new file mode 100644 index 00000000..85ebbf26 --- /dev/null +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/SpatDecoder.java @@ -0,0 +1,135 @@ +package us.dot.its.jpo.ode.api.asn1; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import us.dot.its.jpo.ode.api.models.messages.SpatDecodedMessage; +import us.dot.its.jpo.ode.api.models.messages.DecodedMessage; +import us.dot.its.jpo.ode.api.models.messages.EncodedMessage; +import us.dot.its.jpo.ode.context.AppContext; +import us.dot.its.jpo.ode.model.Asn1Encoding; +import us.dot.its.jpo.ode.model.Asn1Encoding.EncodingRule; +import us.dot.its.jpo.ode.model.OdeAsn1Data; +import us.dot.its.jpo.ode.model.OdeAsn1Payload; +import us.dot.its.jpo.ode.model.OdeSpatData; +import us.dot.its.jpo.ode.model.OdeSpatMetadata; +import us.dot.its.jpo.ode.model.OdeSpatMetadata.SpatSource; +import us.dot.its.jpo.ode.model.OdeSpatPayload; +import us.dot.its.jpo.ode.model.OdeData; +import us.dot.its.jpo.ode.model.OdeHexByteArray; +import us.dot.its.jpo.ode.model.OdeLogMetadata.RecordType; +import us.dot.its.jpo.ode.model.OdeLogMetadata.SecurityResultCode; +import us.dot.its.jpo.ode.model.OdeLogMsgMetadataLocation; +import us.dot.its.jpo.ode.model.OdeMsgPayload; +import us.dot.its.jpo.ode.model.ReceivedMessageDetails; +import us.dot.its.jpo.ode.model.RxSource; +import us.dot.its.jpo.ode.plugin.j2735.builders.SPATBuilder; +import us.dot.its.jpo.ode.util.JsonUtils; +import us.dot.its.jpo.ode.util.XmlUtils; +import us.dot.its.jpo.ode.util.XmlUtils.XmlUtilsException; + +public class SpatDecoder implements Decoder { + + + @Override + public DecodedMessage decode(EncodedMessage message) { + + // Convert to Ode Data type and Add Metadata + OdeData data = getAsOdeData(message.getAsn1Message()); + + XmlUtils xmlUtils = new XmlUtils(); + + try { + // Convert to XML for ASN.1 Decoder + String xml = xmlUtils.toXml(data); + + // Send String through ASN.1 Decoder to get Decoded XML Data + String decodedXml = DecoderManager.decodeXmlWithAcm(xml); + + + // Convert to Ode Json + OdeSpatData spat = getAsOdeJson(decodedXml); + + // build output data structure + DecodedMessage decodedMessage = new SpatDecodedMessage(null, spat, message.getAsn1Message(), ""); + return decodedMessage; + + } catch (JsonProcessingException e) { + e.printStackTrace(); + return new SpatDecodedMessage(null, null, message.getAsn1Message(), e.getMessage()); + } catch (Exception e) { + e.printStackTrace(); + return new SpatDecodedMessage(null, null, message.getAsn1Message(), e.getMessage()); + } + } + + @Override + public OdeData getAsOdeData(String encodedData) { + OdeMsgPayload payload = new OdeAsn1Payload(new OdeHexByteArray(encodedData)); + + // construct metadata + OdeSpatMetadata metadata = new OdeSpatMetadata(payload); + + metadata.setOdeReceivedAt(DecoderManager.getOdeReceivedAt()); + metadata.setOriginIp(DecoderManager.getOriginIp()); + metadata.setRecordType(RecordType.spatTx); + metadata.setSecurityResultCode(SecurityResultCode.success); + + // construct metadata: receivedMessageDetails + ReceivedMessageDetails receivedMessageDetails = new ReceivedMessageDetails(); + receivedMessageDetails.setRxSource(RxSource.NA); + + // construct metadata: locationData + OdeLogMsgMetadataLocation locationData = new OdeLogMsgMetadataLocation(); + receivedMessageDetails.setLocationData(locationData); + + metadata.setReceivedMessageDetails(receivedMessageDetails); + metadata.setSpatSource(SpatSource.V2X); + + Asn1Encoding unsecuredDataEncoding = new Asn1Encoding("unsecuredData", "MessageFrame", + EncodingRule.UPER); + metadata.addEncoding(unsecuredDataEncoding); + + // construct odeData + return new OdeAsn1Data(metadata, payload); + } + + @Override + public OdeSpatData getAsOdeJson(String consumedData) throws XmlUtilsException { + ObjectNode consumed = XmlUtils.toObjectNode(consumedData); + + JsonNode metadataNode = consumed.findValue(AppContext.METADATA_STRING); + if (metadataNode instanceof ObjectNode) { + ObjectNode object = (ObjectNode) metadataNode; + object.remove(AppContext.ENCODINGS_STRING); + + //Spat header file does not have a location and use predefined set required RxSource + ReceivedMessageDetails receivedMessageDetails = new ReceivedMessageDetails(); + receivedMessageDetails.setRxSource(RxSource.NA); + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode jsonNode; + try { + jsonNode = objectMapper.readTree(receivedMessageDetails.toJson()); + object.set(AppContext.RECEIVEDMSGDETAILS_STRING, jsonNode); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + } + + OdeSpatMetadata metadata = (OdeSpatMetadata) JsonUtils.fromJson(metadataNode.toString(), OdeSpatMetadata.class); + + if(metadataNode.findValue("certPresent") != null) { + boolean isCertPresent = metadataNode.findValue("certPresent").asBoolean(); + metadata.setIsCertPresent(isCertPresent); + } + + if (metadata.getSchemaVersion() <= 4) { + metadata.setReceivedMessageDetails(null); + } + + OdeSpatPayload payload = new OdeSpatPayload(SPATBuilder.genericSPAT(consumed.findValue("SPAT"))); + return new OdeSpatData(metadata, payload); + } + +} diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/SrmDecoder.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/SrmDecoder.java new file mode 100644 index 00000000..0892478a --- /dev/null +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/SrmDecoder.java @@ -0,0 +1,119 @@ +package us.dot.its.jpo.ode.api.asn1; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import us.dot.its.jpo.ode.api.models.messages.SrmDecodedMessage; +import us.dot.its.jpo.ode.api.models.messages.DecodedMessage; +import us.dot.its.jpo.ode.api.models.messages.EncodedMessage; +import us.dot.its.jpo.ode.context.AppContext; +import us.dot.its.jpo.ode.model.Asn1Encoding; +import us.dot.its.jpo.ode.model.Asn1Encoding.EncodingRule; +import us.dot.its.jpo.ode.model.OdeAsn1Data; +import us.dot.its.jpo.ode.model.OdeAsn1Payload; +import us.dot.its.jpo.ode.model.OdeSrmData; +import us.dot.its.jpo.ode.model.OdeSrmMetadata; +import us.dot.its.jpo.ode.model.OdeSrmPayload; +import us.dot.its.jpo.ode.model.OdeData; +import us.dot.its.jpo.ode.model.OdeHexByteArray; +import us.dot.its.jpo.ode.model.OdeLogMetadata.RecordType; +import us.dot.its.jpo.ode.model.OdeMsgPayload; +import us.dot.its.jpo.ode.model.ReceivedMessageDetails; +import us.dot.its.jpo.ode.model.RxSource; +import us.dot.its.jpo.ode.plugin.j2735.builders.SRMBuilder; +import us.dot.its.jpo.ode.util.JsonUtils; +import us.dot.its.jpo.ode.util.XmlUtils; +import us.dot.its.jpo.ode.util.XmlUtils.XmlUtilsException; + +public class SrmDecoder implements Decoder { + + + @Override + public DecodedMessage decode(EncodedMessage message) { + + // Convert to Ode Data type and Add Metadata + OdeData data = getAsOdeData(message.getAsn1Message()); + + XmlUtils xmlUtils = new XmlUtils(); + + try { + // Convert to XML for ASN.1 Decoder + String xml = xmlUtils.toXml(data); + + // Send String through ASN.1 Decoder to get Decoded XML Data + String decodedXml = DecoderManager.decodeXmlWithAcm(xml); + + + // Convert to Ode Json + OdeSrmData srm = getAsOdeJson(decodedXml); + + // build output data structure + DecodedMessage decodedMessage = new SrmDecodedMessage(srm, message.getAsn1Message(), ""); + return decodedMessage; + + } catch (JsonProcessingException e) { + e.printStackTrace(); + return new SrmDecodedMessage(null, message.getAsn1Message(), e.getMessage()); + } catch (Exception e) { + e.printStackTrace(); + return new SrmDecodedMessage(null, message.getAsn1Message(), e.getMessage()); + } + } + + @Override + public OdeData getAsOdeData(String encodedData) { + OdeMsgPayload payload = new OdeAsn1Payload(new OdeHexByteArray(encodedData)); + + // construct metadata + OdeSrmMetadata metadata = new OdeSrmMetadata(payload); + + //construct metadata + metadata = new OdeSrmMetadata(payload); + metadata.setOdeReceivedAt(DecoderManager.getOdeReceivedAt()); + metadata.setOriginIp(DecoderManager.getOriginIp()); + metadata.setRecordType(RecordType.srmTx); + + Asn1Encoding unsecuredDataEncoding = new Asn1Encoding("unsecuredData", "MessageFrame",EncodingRule.UPER); + metadata.addEncoding(unsecuredDataEncoding); + + //construct odeData + return new OdeAsn1Data(metadata, payload); + + } + + @Override + public OdeSrmData getAsOdeJson(String consumedData) throws XmlUtilsException { + ObjectNode consumed = XmlUtils.toObjectNode(consumedData); + + JsonNode metadataNode = consumed.findValue(AppContext.METADATA_STRING); + if (metadataNode instanceof ObjectNode) { + ObjectNode object = (ObjectNode) metadataNode; + object.remove(AppContext.ENCODINGS_STRING); + + // Ssm header file does not have a location and use predefined set required + // RxSource + ReceivedMessageDetails receivedMessageDetails = new ReceivedMessageDetails(); + receivedMessageDetails.setRxSource(RxSource.NA); + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode jsonNode; + try { + jsonNode = objectMapper.readTree(receivedMessageDetails.toJson()); + object.set(AppContext.RECEIVEDMSGDETAILS_STRING, jsonNode); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + } + + OdeSrmMetadata metadata = (OdeSrmMetadata) JsonUtils.fromJson(metadataNode.toString(), OdeSrmMetadata.class); + + if (metadata.getSchemaVersion() <= 4) { + metadata.setReceivedMessageDetails(null); + } + + OdeSrmPayload payload = new OdeSrmPayload(SRMBuilder.genericSRM(consumed.findValue("SignalRequestMessage"))); + return new OdeSrmData(metadata, payload); + } + + +} diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/SsmDecoder.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/SsmDecoder.java new file mode 100644 index 00000000..7b95c406 --- /dev/null +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/SsmDecoder.java @@ -0,0 +1,117 @@ +package us.dot.its.jpo.ode.api.asn1; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import us.dot.its.jpo.ode.api.models.messages.SsmDecodedMessage; +import us.dot.its.jpo.ode.api.models.messages.DecodedMessage; +import us.dot.its.jpo.ode.api.models.messages.EncodedMessage; +import us.dot.its.jpo.ode.context.AppContext; +import us.dot.its.jpo.ode.model.Asn1Encoding; +import us.dot.its.jpo.ode.model.Asn1Encoding.EncodingRule; +import us.dot.its.jpo.ode.model.OdeAsn1Data; +import us.dot.its.jpo.ode.model.OdeAsn1Payload; +import us.dot.its.jpo.ode.model.OdeSsmData; +import us.dot.its.jpo.ode.model.OdeSsmMetadata; +import us.dot.its.jpo.ode.model.OdeSsmPayload; +import us.dot.its.jpo.ode.model.OdeData; +import us.dot.its.jpo.ode.model.OdeHexByteArray; +import us.dot.its.jpo.ode.model.OdeLogMetadata.RecordType; +import us.dot.its.jpo.ode.model.OdeMsgPayload; +import us.dot.its.jpo.ode.model.ReceivedMessageDetails; +import us.dot.its.jpo.ode.model.RxSource; +import us.dot.its.jpo.ode.plugin.j2735.builders.SSMBuilder; +import us.dot.its.jpo.ode.util.JsonUtils; +import us.dot.its.jpo.ode.util.XmlUtils; +import us.dot.its.jpo.ode.util.XmlUtils.XmlUtilsException; + +public class SsmDecoder implements Decoder { + + + @Override + public DecodedMessage decode(EncodedMessage message) { + + // Convert to Ode Data type and Add Metadata + OdeData data = getAsOdeData(message.getAsn1Message()); + + XmlUtils xmlUtils = new XmlUtils(); + + try { + // Convert to XML for ASN.1 Decoder + String xml = xmlUtils.toXml(data); + + // Send String through ASN.1 Decoder to get Decoded XML Data + String decodedXml = DecoderManager.decodeXmlWithAcm(xml); + + + // Convert to Ode Json + OdeSsmData ssm = getAsOdeJson(decodedXml); + + // build output data structure + DecodedMessage decodedMessage = new SsmDecodedMessage(ssm, message.getAsn1Message(), ""); + return decodedMessage; + + } catch (JsonProcessingException e) { + e.printStackTrace(); + return new SsmDecodedMessage(null, message.getAsn1Message(), e.getMessage()); + } catch (Exception e) { + e.printStackTrace(); + return new SsmDecodedMessage(null, message.getAsn1Message(), e.getMessage()); + } + } + + @Override + public OdeData getAsOdeData(String encodedData) { + OdeMsgPayload payload = new OdeAsn1Payload(new OdeHexByteArray(encodedData)); + + // construct metadata + OdeSsmMetadata metadata = new OdeSsmMetadata(payload); + + metadata.setOdeReceivedAt(DecoderManager.getOdeReceivedAt()); + metadata.setOriginIp(DecoderManager.getOriginIp()); + metadata.setRecordType(RecordType.ssmTx); + + Asn1Encoding unsecuredDataEncoding = new Asn1Encoding("unsecuredData", "MessageFrame",EncodingRule.UPER); + metadata.addEncoding(unsecuredDataEncoding); + + //construct odeData + return new OdeAsn1Data(metadata, payload); + + } + + @Override + public OdeSsmData getAsOdeJson(String consumedData) throws XmlUtilsException { + ObjectNode consumed = XmlUtils.toObjectNode(consumedData); + + JsonNode metadataNode = consumed.findValue(AppContext.METADATA_STRING); + if (metadataNode instanceof ObjectNode) { + ObjectNode object = (ObjectNode) metadataNode; + object.remove(AppContext.ENCODINGS_STRING); + + // Ssm header file does not have a location and use predefined set required + // RxSource + ReceivedMessageDetails receivedMessageDetails = new ReceivedMessageDetails(); + receivedMessageDetails.setRxSource(RxSource.NA); + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode jsonNode; + try { + jsonNode = objectMapper.readTree(receivedMessageDetails.toJson()); + object.set(AppContext.RECEIVEDMSGDETAILS_STRING, jsonNode); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + } + + OdeSsmMetadata metadata = (OdeSsmMetadata) JsonUtils.fromJson(metadataNode.toString(), OdeSsmMetadata.class); + + if (metadata.getSchemaVersion() <= 4) { + metadata.setReceivedMessageDetails(null); + } + + OdeSsmPayload payload = new OdeSsmPayload(SSMBuilder.genericSSM(consumed.findValue("SignalStatusMessage"))); + return new OdeSsmData(metadata, payload); + } + + +} diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/TimDecoder.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/TimDecoder.java new file mode 100644 index 00000000..23ab2b38 --- /dev/null +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/TimDecoder.java @@ -0,0 +1,117 @@ +package us.dot.its.jpo.ode.api.asn1; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import us.dot.its.jpo.ode.api.models.messages.TimDecodedMessage; +import us.dot.its.jpo.ode.api.models.messages.DecodedMessage; +import us.dot.its.jpo.ode.api.models.messages.EncodedMessage; +import us.dot.its.jpo.ode.context.AppContext; +import us.dot.its.jpo.ode.model.Asn1Encoding; +import us.dot.its.jpo.ode.model.Asn1Encoding.EncodingRule; +import us.dot.its.jpo.ode.model.OdeAsn1Data; +import us.dot.its.jpo.ode.model.OdeAsn1Payload; +import us.dot.its.jpo.ode.model.OdeTimData; +import us.dot.its.jpo.ode.model.OdeTimMetadata; +import us.dot.its.jpo.ode.model.OdeTimPayload; +import us.dot.its.jpo.ode.model.OdeData; +import us.dot.its.jpo.ode.model.OdeHexByteArray; +import us.dot.its.jpo.ode.model.OdeLogMetadata.RecordType; +import us.dot.its.jpo.ode.model.OdeMsgMetadata.GeneratedBy; +import us.dot.its.jpo.ode.model.OdeMsgPayload; +import us.dot.its.jpo.ode.util.JsonUtils; +import us.dot.its.jpo.ode.util.XmlUtils; +import us.dot.its.jpo.ode.util.XmlUtils.XmlUtilsException; + +public class TimDecoder implements Decoder { + + + @Override + public DecodedMessage decode(EncodedMessage message) { + + // Convert to Ode Data type and Add Metadata + OdeData data = getAsOdeData(message.getAsn1Message()); + + XmlUtils xmlUtils = new XmlUtils(); + + try { + // Convert to XML for ASN.1 Decoder + String xml = xmlUtils.toXml(data); + + // Send String through ASN.1 Decoder to get Decoded XML Data + String decodedXml = DecoderManager.decodeXmlWithAcm(xml); + + + // Convert to Ode Json + OdeTimData tim = getAsOdeJson(decodedXml); + + // build output data structure + DecodedMessage decodedMessage = new TimDecodedMessage(tim, message.getAsn1Message(), ""); + return decodedMessage; + + } catch (JsonProcessingException e) { + e.printStackTrace(); + return new TimDecodedMessage(null, message.getAsn1Message(), e.getMessage()); + } catch (Exception e) { + e.printStackTrace(); + return new TimDecodedMessage(null, message.getAsn1Message(), e.getMessage()); + } + } + + @Override + public OdeData getAsOdeData(String encodedData) { + OdeMsgPayload payload = new OdeAsn1Payload(new OdeHexByteArray(encodedData)); + + // construct metadata + OdeTimMetadata metadata = new OdeTimMetadata(payload); + + //construct metadata + metadata = new OdeTimMetadata(payload); + metadata.setOdeReceivedAt(DecoderManager.getOdeReceivedAt()); + metadata.setOriginIp("user-upload"); + metadata.setRecordType(RecordType.timMsg); + metadata.setRecordGeneratedBy(GeneratedBy.RSU); + + Asn1Encoding unsecuredDataEncoding = new Asn1Encoding("unsecuredData", "MessageFrame",EncodingRule.UPER); + metadata.addEncoding(unsecuredDataEncoding); + + //construct odeData + return new OdeAsn1Data(metadata, payload); + } + + @Override + public OdeTimData getAsOdeJson(String consumedData) throws XmlUtilsException { + ObjectNode consumed = XmlUtils.toObjectNode(consumedData); + + JsonNode metadataNode = consumed.findValue(AppContext.METADATA_STRING); + if (metadataNode instanceof ObjectNode) { + ObjectNode object = (ObjectNode) metadataNode; + object.remove(AppContext.ENCODINGS_STRING); + } + + JsonNode payloadNode = consumed.findValue("TravelerInformationMessage"); + if (payloadNode instanceof ObjectNode) { + ObjectNode object = (ObjectNode) payloadNode; + object.remove(AppContext.ENCODINGS_STRING); + } + + OdeTimMetadata metadata = (OdeTimMetadata) JsonUtils.fromJson( + metadataNode.toString(), OdeTimMetadata.class); + + OdeTimPayload payload = (OdeTimPayload) JsonUtils.fromJson( + payloadNode.toString(), OdeTimPayload.class); + + /* + * ODE-755 and ODE-765 Starting with schemaVersion=5 receivedMessageDetails + * will be present in BSM metadata. None should be present in prior versions. + */ + if (metadata.getSchemaVersion() <= 4) { + metadata.setReceivedMessageDetails(null); + } + + // OdeTimPayload payload = new OdeTimPayload( + // TimBuilder.genericTim(consumed.findValue("TravelerInformationMessage"))); + return new OdeTimData(metadata, payload); + } + +} diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/controllers/DecoderController.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/controllers/DecoderController.java index f00a83d9..1bb0ddb3 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/controllers/DecoderController.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/controllers/DecoderController.java @@ -1,10 +1,9 @@ package us.dot.its.jpo.ode.api.controllers; - + import java.time.ZonedDateTime; - // import jakarta.ws.rs.core.Response; - + import org.apache.commons.lang3.exception.ExceptionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -17,6 +16,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import us.dot.its.jpo.ode.api.models.MessageType; +import us.dot.its.jpo.ode.api.models.TypePayload; +import us.dot.its.jpo.ode.api.models.messages.DecodedMessage; import us.dot.its.jpo.ode.api.models.messages.EncodedMessage; import us.dot.its.jpo.ode.mockdata.MockDecodedMessageGenerator; import org.springframework.http.HttpStatus; @@ -25,48 +26,53 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.ResponseBody; import us.dot.its.jpo.ode.api.ConflictMonitorApiProperties; +import us.dot.its.jpo.ode.api.asn1.DecoderManager; // import us.dot.its.jpo.ode.coder.StringPublisher; - + @RestController public class DecoderController { - + private static final Logger logger = LoggerFactory.getLogger(AssessmentController.class); - + ObjectMapper objectMapper = new ObjectMapper(); - + @Autowired ConflictMonitorApiProperties props; - // private StringPublisher bsmPublisher; - + public String getCurrentTime() { return ZonedDateTime.now().toInstant().toEpochMilli() + ""; } - + // @CrossOrigin(origins = "http://localhost:3000") - // @RequestMapping(value = "/decoder/upload", method = RequestMethod.POST, produces = "application/json") + // @RequestMapping(value = "/decoder/upload", method = RequestMethod.POST, + // produces = "application/json") // public @ResponseBody ResponseEntity new_bulk_upload_request( - // @RequestBody UploadData newUploadData, - // @RequestParam(name = "test", required = false, defaultValue = "false") boolean testData) { - // try { - // logger.info("Uploading Bulk Data"); - - // if (testData) { - // newUploadData = MockUploadDataGenerator.getUploadData(); - // } else { - // return ResponseEntity.status(HttpStatus.NOT_IMPLEMENTED).contentType(MediaType.TEXT_PLAIN) - // .body(newUploadData.toString()); - // } - - // return ResponseEntity.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON) - // .body(newUploadData.toString()); - // } catch (Exception e) { - // logger.info("Failed to Upload Bulk Data"); - // return ResponseEntity.status(HttpStatus.BAD_REQUEST).contentType(MediaType.TEXT_PLAIN) - // .body(ExceptionUtils.getStackTrace(e)); - // } + // @RequestBody UploadData newUploadData, + // @RequestParam(name = "test", required = false, defaultValue = "false") + // boolean testData) { + // try { + // logger.info("Uploading Bulk Data"); + + // if (testData) { + // newUploadData = MockUploadDataGenerator.getUploadData(); + // } else { + // return + // ResponseEntity.status(HttpStatus.NOT_IMPLEMENTED).contentType(MediaType.TEXT_PLAIN) + // .body(newUploadData.toString()); + // } + + // return + // ResponseEntity.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON) + // .body(newUploadData.toString()); + // } catch (Exception e) { + // logger.info("Failed to Upload Bulk Data"); + // return + // ResponseEntity.status(HttpStatus.BAD_REQUEST).contentType(MediaType.TEXT_PLAIN) + // .body(ExceptionUtils.getStackTrace(e)); + // } // } @CrossOrigin(origins = "http://localhost:3000") @@ -76,36 +82,49 @@ public String getCurrentTime() { @RequestParam(name = "test", required = false, defaultValue = "false") boolean testData) { try { if (testData) { - if(encodedMessage.getType() == MessageType.BSM || encodedMessage.getType() == MessageType.UNKNOWN){ + if (encodedMessage.getType() == MessageType.BSM || encodedMessage.getType() == MessageType.UNKNOWN) { return ResponseEntity.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON) - .body(MockDecodedMessageGenerator.getBsmDecodedMessage().toString()); - } - else if(encodedMessage.getType() == MessageType.MAP){ + .body(MockDecodedMessageGenerator.getBsmDecodedMessage().toString()); + } else if (encodedMessage.getType() == MessageType.MAP) { return ResponseEntity.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON) - .body(MockDecodedMessageGenerator.getMapDecodedMessage().toString()); - }else if(encodedMessage.getType() == MessageType.SPAT){ + .body(MockDecodedMessageGenerator.getMapDecodedMessage().toString()); + } else if (encodedMessage.getType() == MessageType.SPAT) { return ResponseEntity.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON) - .body(MockDecodedMessageGenerator.getSpatDecodedMessage().toString()); - }else if(encodedMessage.getType() == MessageType.SRM){ + .body(MockDecodedMessageGenerator.getSpatDecodedMessage().toString()); + } else if (encodedMessage.getType() == MessageType.SRM) { return ResponseEntity.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON) - .body(MockDecodedMessageGenerator.getSpatDecodedMessage().toString()); - }else if(encodedMessage.getType() == MessageType.SSM){ + .body(MockDecodedMessageGenerator.getSpatDecodedMessage().toString()); + } else if (encodedMessage.getType() == MessageType.SSM) { return ResponseEntity.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON) - .body(MockDecodedMessageGenerator.getSpatDecodedMessage().toString()); - }else if(encodedMessage.getType() == MessageType.TIM){ + .body(MockDecodedMessageGenerator.getSpatDecodedMessage().toString()); + } else if (encodedMessage.getType() == MessageType.TIM) { return ResponseEntity.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON) - .body(MockDecodedMessageGenerator.getSpatDecodedMessage().toString()); - }else{ + .body(MockDecodedMessageGenerator.getSpatDecodedMessage().toString()); + } else { return ResponseEntity.status(HttpStatus.BAD_REQUEST).contentType(MediaType.TEXT_PLAIN) - .body("No available mapping for Message Type " + encodedMessage.getType()); + .body("No available mapping for Message Type " + encodedMessage.getType()); } } else { - return ResponseEntity.status(HttpStatus.NOT_IMPLEMENTED).contentType(MediaType.TEXT_PLAIN) - .body("Not Implemented Yet"); + + if (encodedMessage.getType() == MessageType.UNKNOWN) { + EncodedMessage newEncodedMessage = DecoderManager.identifyAsn1(encodedMessage.getAsn1Message()); + + if (newEncodedMessage.getType() != MessageType.UNKNOWN) { + encodedMessage = newEncodedMessage; + } else { + return ResponseEntity.status(HttpStatus.OK) + .contentType(MediaType.TEXT_PLAIN) + .body(new DecodedMessage(encodedMessage.getAsn1Message(), MessageType.UNKNOWN, + "Unable to identify Message Type from ASN.1").toString()); + } + } + + DecodedMessage decodedMessage = DecoderManager.decode(encodedMessage); + + return ResponseEntity.status(HttpStatus.OK).contentType(MediaType.TEXT_PLAIN) + .body(decodedMessage.toString()); } - - // return ResponseEntity.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON) - // .body(newUploadData.toString()); + } catch (Exception e) { logger.info("Failed to Upload Data"); return ResponseEntity.status(HttpStatus.BAD_REQUEST).contentType(MediaType.TEXT_PLAIN) @@ -114,31 +133,32 @@ else if(encodedMessage.getType() == MessageType.MAP){ } // public void publishBSM(String asn1Bsm){ - // String payloadHexString = HexUtils.toHexString(payload); - // logger.debug("Packet: {}", payloadHexString); - - // // Add header data for the decoding process - // ZonedDateTime utc = ZonedDateTime.now(ZoneOffset.UTC); - // String timestamp = utc.format(DateTimeFormatter.ISO_INSTANT); + // String payloadHexString = HexUtils.toHexString(payload); + // logger.debug("Packet: {}", payloadHexString); + + // // Add header data for the decoding process + // ZonedDateTime utc = ZonedDateTime.now(ZoneOffset.UTC); + // String timestamp = utc.format(DateTimeFormatter.ISO_INSTANT); - // JSONObject metadataObject = new JSONObject(); - // metadataObject.put("utctimestamp", timestamp); - // metadataObject.put("originRsu", senderIp); + // JSONObject metadataObject = new JSONObject(); + // metadataObject.put("utctimestamp", timestamp); + // metadataObject.put("originRsu", senderIp); - // JSONObject messageObject = new JSONObject(); - // messageObject.put("metadata", metadataObject); - // messageObject.put("payload", payloadHexString); + // JSONObject messageObject = new JSONObject(); + // messageObject.put("metadata", metadataObject); + // messageObject.put("payload", payloadHexString); - // JSONArray messageList = new JSONArray(); - // messageList.put(messageObject); + // JSONArray messageList = new JSONArray(); + // messageList.put(messageObject); - // JSONObject jsonObject = new JSONObject(); - // jsonObject.put("BsmMessageContent", messageList); + // JSONObject jsonObject = new JSONObject(); + // jsonObject.put("BsmMessageContent", messageList); - // logger.debug("BSM JSON Object: {}", jsonObject.toString()); + // logger.debug("BSM JSON Object: {}", jsonObject.toString()); - // // Submit JSON to the OdeRawEncodedMessageJson Kafka Topic - // this.bsmPublisher.publish(jsonObject.toString(), this.bsmPublisher.getOdeProperties().getKafkaTopicOdeRawEncodedBSMJson()); + // // Submit JSON to the OdeRawEncodedMessageJson Kafka Topic + // this.bsmPublisher.publish(jsonObject.toString(), + // this.bsmPublisher.getOdeProperties().getKafkaTopicOdeRawEncodedBSMJson()); // } } \ No newline at end of file diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/BsmDecodedMessage.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/BsmDecodedMessage.java index 276ea0ba..58d108c6 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/BsmDecodedMessage.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/BsmDecodedMessage.java @@ -12,8 +12,8 @@ public class BsmDecodedMessage extends DecodedMessage{ public OdeBsmData bsm; - public BsmDecodedMessage(OdeBsmData bsm, String asn1Text, MessageType type, String decodeErrors) { - super(asn1Text, type, decodeErrors); + public BsmDecodedMessage(OdeBsmData bsm, String asn1Text, String decodeErrors) { + super(asn1Text, MessageType.BSM, decodeErrors); this.bsm = bsm; } diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/EncodedMessage.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/EncodedMessage.java index 90eeb9da..99cee375 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/EncodedMessage.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/EncodedMessage.java @@ -15,6 +15,10 @@ public class EncodedMessage { String asn1Message; MessageType type; + public EncodedMessage(){ + + } + public EncodedMessage(String asn1Message, MessageType type){ this.asn1Message = asn1Message; this.type = type; diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/MapDecodedMessage.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/MapDecodedMessage.java index 91608b9f..66e286f0 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/MapDecodedMessage.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/MapDecodedMessage.java @@ -16,8 +16,8 @@ public class MapDecodedMessage extends DecodedMessage{ public OdeMapData map; public ProcessedMap processedMap; - public MapDecodedMessage(ProcessedMap processedMap, OdeMapData map, String asn1Text, MessageType type, String decodeErrors) { - super(asn1Text, type, decodeErrors); + public MapDecodedMessage(ProcessedMap processedMap, OdeMapData map, String asn1Text, String decodeErrors) { + super(asn1Text, MessageType.MAP, decodeErrors); this.processedMap = processedMap; this.map = map; } diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SpatDecodedMessage.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SpatDecodedMessage.java index 375c96eb..a938c8e5 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SpatDecodedMessage.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SpatDecodedMessage.java @@ -14,8 +14,8 @@ public class SpatDecodedMessage extends DecodedMessage{ public OdeSpatData spat; public ProcessedSpat processedSpat; - public SpatDecodedMessage(ProcessedSpat processedSpat, OdeSpatData spat, String asn1Text, MessageType type, String decodeErrors) { - super(asn1Text, type, decodeErrors); + public SpatDecodedMessage(ProcessedSpat processedSpat, OdeSpatData spat, String asn1Text, String decodeErrors) { + super(asn1Text, MessageType.SPAT, decodeErrors); this.processedSpat = processedSpat; this.spat = spat; diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SrmDecodedMessage.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SrmDecodedMessage.java index 2281c628..2cb1549e 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SrmDecodedMessage.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SrmDecodedMessage.java @@ -12,8 +12,8 @@ public class SrmDecodedMessage extends DecodedMessage{ public OdeSrmData srm; - public SrmDecodedMessage(OdeSrmData srm, String asn1Text, MessageType type, String decodeErrors) { - super(asn1Text, type, decodeErrors); + public SrmDecodedMessage(OdeSrmData srm, String asn1Text, String decodeErrors) { + super(asn1Text, MessageType.SRM, decodeErrors); this.srm = srm; } diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SsmDecodedMessage.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SsmDecodedMessage.java index 82759dab..51718965 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SsmDecodedMessage.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SsmDecodedMessage.java @@ -12,8 +12,8 @@ public class SsmDecodedMessage extends DecodedMessage{ public OdeSsmData ssm; - public SsmDecodedMessage(OdeSsmData ssm, String asn1Text, MessageType type, String decodeErrors) { - super(asn1Text, type, decodeErrors); + public SsmDecodedMessage(OdeSsmData ssm, String asn1Text, String decodeErrors) { + super(asn1Text, MessageType.SSM, decodeErrors); this.ssm = ssm; } diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/TimDecodedMessage.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/TimDecodedMessage.java index f57d85b0..e771ac90 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/TimDecodedMessage.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/TimDecodedMessage.java @@ -12,8 +12,8 @@ public class TimDecodedMessage extends DecodedMessage{ public OdeTimData tim; - public TimDecodedMessage(OdeTimData tim, String asn1Text, MessageType type, String decodeErrors) { - super(asn1Text, type, decodeErrors); + public TimDecodedMessage(OdeTimData tim, String asn1Text, String decodeErrors) { + super(asn1Text, MessageType.TIM, decodeErrors); this.tim = tim; } diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockDecodedMessageGenerator.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockDecodedMessageGenerator.java index 92c6be7b..cece1836 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockDecodedMessageGenerator.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockDecodedMessageGenerator.java @@ -1,6 +1,5 @@ package us.dot.its.jpo.ode.mockdata; -import us.dot.its.jpo.ode.api.models.MessageType; import us.dot.its.jpo.ode.api.models.messages.BsmDecodedMessage; import us.dot.its.jpo.ode.api.models.messages.MapDecodedMessage; import us.dot.its.jpo.ode.api.models.messages.SpatDecodedMessage; @@ -12,32 +11,32 @@ public class MockDecodedMessageGenerator { public static BsmDecodedMessage getBsmDecodedMessage(){ - BsmDecodedMessage message = new BsmDecodedMessage(MockBsmGenerator.getJsonBsms().get(0),"00145144ad0b7947c2ed9ad2748035a4e8ff880000000fd2229199307d7d07d0b17fff05407d12720038c000fe72c107b001ea88fffeb4002127c0009000000fdfffe3ffff9407344704000041910120100000000efc10609c26e900e11f61a947802127c0009000000fdfffe3ffff9407453304000041910120100000008ffffe501ca508100000000000a508100000404804000000849f00024000003f7fff8ffffe501ca508100000fe501ca508100000fffe501ca51c10000000024000003f7fff8ffffe501ca51c1" , MessageType.BSM, ""); + BsmDecodedMessage message = new BsmDecodedMessage(MockBsmGenerator.getJsonBsms().get(0),"00145144ad0b7947c2ed9ad2748035a4e8ff880000000fd2229199307d7d07d0b17fff05407d12720038c000fe72c107b001ea88fffeb4002127c0009000000fdfffe3ffff9407344704000041910120100000000efc10609c26e900e11f61a947802127c0009000000fdfffe3ffff9407453304000041910120100000008ffffe501ca508100000000000a508100000404804000000849f00024000003f7fff8ffffe501ca508100000fe501ca508100000fffe501ca51c10000000024000003f7fff8ffffe501ca51c1" , ""); return message; } public static MapDecodedMessage getMapDecodedMessage(){ - MapDecodedMessage message = new MapDecodedMessage(MockMapGenerator.getProcessedMaps().get(0),MockMapGenerator.getJsonMaps().get(0),"0012839338023000205e96094d40df4c2ca626c8516e02dc3c2010640000000289e01c009f603f42e88039900000000a41107b027d80fd0a4200c6400000002973021c09f603de0c16029200000080002a8a008d027d98fee805404fb0e1085f60588200028096021200000080002aa0007d027d98fe9802e04fb1200c214456228000a02b1240005022c03240000020000d56b40bc04fb35ff655e2c09f623fb81c835fec0db240a0a2bff4aebf82c660000804b0089000000800025670034013ecd7fb9578e027d9aff883c4e050515ffa567a41635000040258024800000400012b8f81f409f663fac094013ecd7fc83ddb02829affa480bc04fb02c6e0000804b09c5000000200035ea98a9604f60da6c7c113d505c35ffe941d409f65c05034c050500c9880004409bc800000006d2bd3cec813c40cde062c1fd400000200008791ea3db3cf380a009f666f05005813d80ffe0a0588c00040092106a00000000bc75cac009f66db54c04a813d80a100801241ed40000000078ebae3b6da7a008809e2050904008811f100000000bc72389009f60eca8002049c400000002f1b2ca3027d93a71fa813ec204bc400000002f1b2b34027b0397608880cd10000000039b8e1a51036820505080d51000000003a7461ed1036760505080dd1000000003b2f62311006260505160bca00000080002b785e2a80a0a6c028de728145037f1f9e456488000202b2540001022c1894000001000057058c5b81414d806dbcd4028a18f4df23a050502c8d0000404b05a5000000800035b6471bc05053602431f380a2864087bdb0141458064ab0d6c00053fc013ec0b0680006012c15940000020000d6c06c6581414d807fb972028a1901d78dc050536020ec1800a0a6c039d639813d80b0780006012c1494000002000096ab8c6581414d8062be32028a1b01417e04050a360172d77009e2058440003009409c200000040006b3486a480a0a1cab7134c8117dcc02879b018fae2c050f3601ced54809e21012720000000067fbad0007e7e84045c80000000100661580958004041c8000000019f3658401cdfa2c0d64000002000144016c02c36ddfff0282984acc1ee05052c36f0ac02828669d82da8f821480a0a10f140002c8e0001004b03190000008000519fd190c43b2e0066108b08401428c342a0ce02828258a0604a6be959aee0e6050502c920001004b02d90000008000459fa164404fb30a8580a00a14619c306701414c32ce10e02829659081f814141029030164b000080200" , MessageType.BSM, ""); + MapDecodedMessage message = new MapDecodedMessage(MockMapGenerator.getProcessedMaps().get(0),MockMapGenerator.getJsonMaps().get(0),"0012839338023000205e96094d40df4c2ca626c8516e02dc3c2010640000000289e01c009f603f42e88039900000000a41107b027d80fd0a4200c6400000002973021c09f603de0c16029200000080002a8a008d027d98fee805404fb0e1085f60588200028096021200000080002aa0007d027d98fe9802e04fb1200c214456228000a02b1240005022c03240000020000d56b40bc04fb35ff655e2c09f623fb81c835fec0db240a0a2bff4aebf82c660000804b0089000000800025670034013ecd7fb9578e027d9aff883c4e050515ffa567a41635000040258024800000400012b8f81f409f663fac094013ecd7fc83ddb02829affa480bc04fb02c6e0000804b09c5000000200035ea98a9604f60da6c7c113d505c35ffe941d409f65c05034c050500c9880004409bc800000006d2bd3cec813c40cde062c1fd400000200008791ea3db3cf380a009f666f05005813d80ffe0a0588c00040092106a00000000bc75cac009f66db54c04a813d80a100801241ed40000000078ebae3b6da7a008809e2050904008811f100000000bc72389009f60eca8002049c400000002f1b2ca3027d93a71fa813ec204bc400000002f1b2b34027b0397608880cd10000000039b8e1a51036820505080d51000000003a7461ed1036760505080dd1000000003b2f62311006260505160bca00000080002b785e2a80a0a6c028de728145037f1f9e456488000202b2540001022c1894000001000057058c5b81414d806dbcd4028a18f4df23a050502c8d0000404b05a5000000800035b6471bc05053602431f380a2864087bdb0141458064ab0d6c00053fc013ec0b0680006012c15940000020000d6c06c6581414d807fb972028a1901d78dc050536020ec1800a0a6c039d639813d80b0780006012c1494000002000096ab8c6581414d8062be32028a1b01417e04050a360172d77009e2058440003009409c200000040006b3486a480a0a1cab7134c8117dcc02879b018fae2c050f3601ced54809e21012720000000067fbad0007e7e84045c80000000100661580958004041c8000000019f3658401cdfa2c0d64000002000144016c02c36ddfff0282984acc1ee05052c36f0ac02828669d82da8f821480a0a10f140002c8e0001004b03190000008000519fd190c43b2e0066108b08401428c342a0ce02828258a0604a6be959aee0e6050502c920001004b02d90000008000459fa164404fb30a8580a00a14619c306701414c32ce10e02829659081f814141029030164b000080200" ,""); return message; } public static SpatDecodedMessage getSpatDecodedMessage(){ - SpatDecodedMessage message = new SpatDecodedMessage(MockSpatGenerator.getProcessedSpats().get(0),MockSpatGenerator.getJsonSpats().get(0),"001338000817a780000089680500204642b342b34802021a15a955a940181190acd0acd20100868555c555c00104342aae2aae002821a155715570" , MessageType.BSM, ""); + SpatDecodedMessage message = new SpatDecodedMessage(MockSpatGenerator.getProcessedSpats().get(0),MockSpatGenerator.getJsonSpats().get(0),"001338000817a780000089680500204642b342b34802021a15a955a940181190acd0acd20100868555c555c00104342aae2aae002821a155715570" , ""); return message; } public static TimDecodedMessage getTimDecodedMessage(){ - TimDecodedMessage message = new TimDecodedMessage(MockTimGenerator.getJsonTims().get(0),"005f498718cca69ec1a04600000100105d9b46ec5be401003a0103810040038081d4001f80d07016da410000000000000bbc2b0f775d9b0309c271431fa166ee0a27fff93f136b8205a0a107fb2ef979f4c5bfaeec97e4ad70c2fb36cd9730becdb355cc2fd2a7556b160b98b46ab98ae62c185fa55efb468d5b4000000004e2863f42cddc144ff7980040401262cdd7b809c509f5c62cdd35519c507b9062cdcee129c505cf262cdca5ff9c50432c62cdc5d3d9c502e3e62cdc13e79c501e9262cdbca2d9c5013ee62cdb80359c500e6a62cdb36299c500bc862cdaec1d9c50093c62cdaa2109c5006ea1080203091a859eeebb36006001830001aad27f4ff7580001aad355e39b5880a30029d6585009ef808332d8d9f80c3855151b38c772f765007967ec1170bcb7937f5cb880a25a52863493bcb87570dbcb5abc6bfb2faec606cfa34eb95a24790b2017366d3aabe7729e" , MessageType.TIM, ""); + TimDecodedMessage message = new TimDecodedMessage(MockTimGenerator.getJsonTims().get(0),"005f498718cca69ec1a04600000100105d9b46ec5be401003a0103810040038081d4001f80d07016da410000000000000bbc2b0f775d9b0309c271431fa166ee0a27fff93f136b8205a0a107fb2ef979f4c5bfaeec97e4ad70c2fb36cd9730becdb355cc2fd2a7556b160b98b46ab98ae62c185fa55efb468d5b4000000004e2863f42cddc144ff7980040401262cdd7b809c509f5c62cdd35519c507b9062cdcee129c505cf262cdca5ff9c50432c62cdc5d3d9c502e3e62cdc13e79c501e9262cdbca2d9c5013ee62cdb80359c500e6a62cdb36299c500bc862cdaec1d9c50093c62cdaa2109c5006ea1080203091a859eeebb36006001830001aad27f4ff7580001aad355e39b5880a30029d6585009ef808332d8d9f80c3855151b38c772f765007967ec1170bcb7937f5cb880a25a52863493bcb87570dbcb5abc6bfb2faec606cfa34eb95a24790b2017366d3aabe7729e" , ""); return message; } public static SrmDecodedMessage getSrmDecodedMessage(){ - SrmDecodedMessage message = new SrmDecodedMessage(MockSrmGenerator.getJsonSrms().get(0),"001d2130000010090bd341080d00855c6c0c6899853000a534f7c24cb29897694759b7c0" , MessageType.SRM, ""); + SrmDecodedMessage message = new SrmDecodedMessage(MockSrmGenerator.getJsonSrms().get(0),"001d2130000010090bd341080d00855c6c0c6899853000a534f7c24cb29897694759b7c0" , ""); return message; } public static SsmDecodedMessage getSsmDecodedMessage(){ - SsmDecodedMessage message = new SsmDecodedMessage(MockSsmGenerator.getJsonSsms().get(0),"001e120000000005e9c04071a26614c06000040ba0" , MessageType.SSM, ""); + SsmDecodedMessage message = new SsmDecodedMessage(MockSsmGenerator.getJsonSsms().get(0),"001e120000000005e9c04071a26614c06000040ba0" , ""); return message; } } From 333fd8aff2de68ca5a17f9fd244f9d476f29aa3a Mon Sep 17 00:00:00 2001 From: John-Wiens Date: Fri, 10 May 2024 08:46:37 -0600 Subject: [PATCH 10/29] Fixed message serializers to include parent information. --- .../jpo/ode/api/ConflictApiApplication.java | 6 +++- .../dot/its/jpo/ode/api/asn1/BsmDecoder.java | 4 +-- .../us/dot/its/jpo/ode/api/asn1/Decoder.java | 2 ++ .../its/jpo/ode/api/asn1/DecoderManager.java | 20 ++++++------- .../models/messages/BsmDecodedMessage.java | 3 +- .../api/models/messages/DecodedMessage.java | 30 +++++++++++++++++-- .../models/messages/MapDecodedMessage.java | 2 -- .../models/messages/SpatDecodedMessage.java | 2 -- .../models/messages/SrmDecodedMessage.java | 2 -- .../models/messages/SsmDecodedMessage.java | 2 -- .../models/messages/TimDecodedMessage.java | 2 -- 11 files changed, 47 insertions(+), 28 deletions(-) diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/ConflictApiApplication.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/ConflictApiApplication.java index 3cd26972..335ce260 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/ConflictApiApplication.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/ConflictApiApplication.java @@ -8,6 +8,7 @@ import us.dot.its.jpo.ode.api.asn1.DecoderManager; import us.dot.its.jpo.ode.api.models.MessageType; import us.dot.its.jpo.ode.api.models.messages.BsmDecodedMessage; +import us.dot.its.jpo.ode.api.models.messages.DecodedMessage; import us.dot.its.jpo.ode.api.models.messages.EncodedMessage; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; import org.springframework.scheduling.annotation.EnableScheduling; @@ -28,7 +29,10 @@ public static void main(String[] args) { System.out.println("Conflict Monitor API docs page found here: http://localhost:8081/swagger-ui/index.html"); System.out.println("Startup Complete"); - // BsmDecodedMessage bsm = (BsmDecodedMessage)DecoderManager.decode(new EncodedMessage("00145144ad0b7947c2ed9ad2748035a4e8ff880000000fd2229199307d7d07d0b17fff05407d12720038c000fe72c107b001ea88fffeb4002127c0009000000fdfffe3ffff9407344704000041910120100000000efc10609c26e900e11f61a947802127c0009000000fdfffe3ffff9407453304000041910120100000008ffffe501ca508100000000000a508100000404804000000849f00024000003f7fff8ffffe501ca508100000fe501ca508100000fffe501ca51c10000000024000003f7fff8ffffe501ca51c1", MessageType.BSM)); + DecodedMessage bsm = DecoderManager.decode(new EncodedMessage("00145144ad0b7947c2ed9ad2748035a4e8ff880000000fd2229199307d7d07d0b17fff05407d12720038c000fe72c107b001ea88fffeb4002127c0009000000fdfffe3ffff9407344704000041910120100000000efc10609c26e900e11f61a947802127c0009000000fdfffe3ffff9407453304000041910120100000008ffffe501ca508100000000000a508100000404804000000849f00024000003f7fff8ffffe501ca508100000fe501ca508100000fffe501ca51c10000000024000003f7fff8ffffe501ca51c1", MessageType.BSM)); + + System.out.println(bsm); + diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/BsmDecoder.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/BsmDecoder.java index 7e176979..59662b38 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/BsmDecoder.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/BsmDecoder.java @@ -44,8 +44,8 @@ public DecodedMessage decode(EncodedMessage message) { String xml = xmlUtils.toXml(data); // Send String through ASN.1 Decoder to get Decoded XML Data - String decodedXml = DecoderManager.decodeXmlWithAcm(xml); - + // String decodedXml = DecoderManager.decodeXmlWithAcm(xml); + String decodedXml = mockDecodeXmlWithAcm(xml); // Convert to Ode Json OdeBsmData bsm = getAsOdeJson(decodedXml); diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/Decoder.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/Decoder.java index 0d1c7059..6b621ffc 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/Decoder.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/Decoder.java @@ -11,4 +11,6 @@ public interface Decoder { public OdeData getAsOdeData(String encodedData); public OdeData getAsOdeJson(String encodedXml) throws XmlUtilsException; + + } diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/DecoderManager.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/DecoderManager.java index 4b6af9b7..632230cb 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/DecoderManager.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/DecoderManager.java @@ -75,24 +75,22 @@ public static DecodedMessage decode(EncodedMessage message){ decoder = new BsmDecoder(); } else if(message.getType() == MessageType.MAP){ - + decoder = new MapDecoder(); }else if(message.getType() == MessageType.SPAT){ - + decoder = new SpatDecoder(); }else if(message.getType() == MessageType.SRM){ - + decoder = new SrmDecoder(); }else if(message.getType() == MessageType.SSM){ - + decoder = new SsmDecoder(); }else if(message.getType() == MessageType.TIM){ - - }else{ - - } - - if(decoder!= null){ - return decoder.decode(message); + decoder = new TimDecoder(); }else{ return new DecodedMessage(payload, message.getType(), "No Valid Decoder found for Message Type"); } + + + return decoder.decode(message); + } diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/BsmDecodedMessage.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/BsmDecodedMessage.java index 58d108c6..d3905537 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/BsmDecodedMessage.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/BsmDecodedMessage.java @@ -4,9 +4,8 @@ import us.dot.its.jpo.ode.model.OdeBsmData; import lombok.Getter; import lombok.Setter; -import lombok.ToString; +// import lombok.ToString; -@ToString @Setter @Getter public class BsmDecodedMessage extends DecodedMessage{ diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/DecodedMessage.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/DecodedMessage.java index 4a2c5ff6..55338e75 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/DecodedMessage.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/DecodedMessage.java @@ -4,16 +4,32 @@ import java.time.Instant; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.core.JsonProcessingException; + import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; -import lombok.ToString; +import us.dot.its.jpo.geojsonconverter.DateJsonMapper; import us.dot.its.jpo.ode.api.models.MessageType; -@ToString @Setter @EqualsAndHashCode @Getter +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.EXISTING_PROPERTY, + property = "type" +) +@JsonSubTypes({ + @JsonSubTypes.Type(value = BsmDecodedMessage.class, name = "BSM"), + @JsonSubTypes.Type(value = MapDecodedMessage.class, name = "MAP"), + @JsonSubTypes.Type(value = SpatDecodedMessage.class, name = "SPAT"), + @JsonSubTypes.Type(value = SrmDecodedMessage.class, name = "SRM"), + @JsonSubTypes.Type(value = SsmDecodedMessage.class, name = "SSM"), + @JsonSubTypes.Type(value = TimDecodedMessage.class, name = "TIM") +}) public class DecodedMessage { String asn1Text; long decodeTime; @@ -26,6 +42,16 @@ public DecodedMessage(String asn1Text, MessageType type, String decodeErrors){ this.decodeErrors = decodeErrors; this.type = type.name(); } + + @Override + public String toString() { + try { + return DateJsonMapper.getInstance().writeValueAsString(this); + } catch (JsonProcessingException e) { + // logger.error(String.format("Exception serializing %s Event to JSON", eventType), e); + } + return ""; + } } diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/MapDecodedMessage.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/MapDecodedMessage.java index 66e286f0..f3cc6985 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/MapDecodedMessage.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/MapDecodedMessage.java @@ -7,9 +7,7 @@ import us.dot.its.jpo.ode.model.OdeMapData; import lombok.Getter; import lombok.Setter; -import lombok.ToString; -@ToString @Setter @Getter public class MapDecodedMessage extends DecodedMessage{ diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SpatDecodedMessage.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SpatDecodedMessage.java index a938c8e5..55425173 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SpatDecodedMessage.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SpatDecodedMessage.java @@ -5,9 +5,7 @@ import us.dot.its.jpo.ode.model.OdeSpatData; import lombok.Getter; import lombok.Setter; -import lombok.ToString; -@ToString @Setter @Getter public class SpatDecodedMessage extends DecodedMessage{ diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SrmDecodedMessage.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SrmDecodedMessage.java index 2cb1549e..b3c775d7 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SrmDecodedMessage.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SrmDecodedMessage.java @@ -4,9 +4,7 @@ import us.dot.its.jpo.ode.model.OdeSrmData; import lombok.Getter; import lombok.Setter; -import lombok.ToString; -@ToString @Setter @Getter public class SrmDecodedMessage extends DecodedMessage{ diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SsmDecodedMessage.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SsmDecodedMessage.java index 51718965..91ba40c5 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SsmDecodedMessage.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/SsmDecodedMessage.java @@ -4,9 +4,7 @@ import us.dot.its.jpo.ode.model.OdeSsmData; import lombok.Getter; import lombok.Setter; -import lombok.ToString; -@ToString @Setter @Getter public class SsmDecodedMessage extends DecodedMessage{ diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/TimDecodedMessage.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/TimDecodedMessage.java index e771ac90..252aad92 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/TimDecodedMessage.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/TimDecodedMessage.java @@ -4,9 +4,7 @@ import us.dot.its.jpo.ode.model.OdeTimData; import lombok.Getter; import lombok.Setter; -import lombok.ToString; -@ToString @Setter @Getter public class TimDecodedMessage extends DecodedMessage{ From 4b907f16a05707fd18587eca1752ceae2aeef7f4 Mon Sep 17 00:00:00 2001 From: John-Wiens Date: Fri, 10 May 2024 10:48:15 -0600 Subject: [PATCH 11/29] Added conversion for ProcessedMap and ProcessedSpat --- .../jpo/ode/api/ConflictApiApplication.java | 20 +++++- .../dot/its/jpo/ode/api/asn1/BsmDecoder.java | 8 ++- .../its/jpo/ode/api/asn1/DecoderManager.java | 36 +++++++++-- .../dot/its/jpo/ode/api/asn1/MapDecoder.java | 59 ++++++++++++++++-- .../dot/its/jpo/ode/api/asn1/SpatDecoder.java | 44 ++++++++++++- .../dot/its/jpo/ode/api/asn1/SrmDecoder.java | 4 ++ .../dot/its/jpo/ode/api/asn1/SsmDecoder.java | 3 + .../dot/its/jpo/ode/api/asn1/TimDecoder.java | 3 + .../api/controllers/DecoderController.java | 4 +- api/jpo-conflictvisualizer-api/test.pdf | Bin 0 -> 227419 bytes 10 files changed, 163 insertions(+), 18 deletions(-) create mode 100644 api/jpo-conflictvisualizer-api/test.pdf diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/ConflictApiApplication.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/ConflictApiApplication.java index 335ce260..5a54a1c8 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/ConflictApiApplication.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/ConflictApiApplication.java @@ -1,5 +1,6 @@ package us.dot.its.jpo.ode.api; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @@ -11,13 +12,18 @@ import us.dot.its.jpo.ode.api.models.messages.DecodedMessage; import us.dot.its.jpo.ode.api.models.messages.EncodedMessage; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; import org.springframework.scheduling.annotation.EnableScheduling; @EnableWebMvc @SpringBootApplication @EnableScheduling +@ComponentScan(basePackages = {"us.dot.its.jpo.ode.api", "us.dot.its.jpo.geojsonconverter.validator"}) public class ConflictApiApplication extends SpringBootServletInitializer { + @Autowired DecoderManager manager; + @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { return builder.sources(ConflictApiApplication.class); @@ -29,12 +35,12 @@ public static void main(String[] args) { System.out.println("Conflict Monitor API docs page found here: http://localhost:8081/swagger-ui/index.html"); System.out.println("Startup Complete"); - DecodedMessage bsm = DecoderManager.decode(new EncodedMessage("00145144ad0b7947c2ed9ad2748035a4e8ff880000000fd2229199307d7d07d0b17fff05407d12720038c000fe72c107b001ea88fffeb4002127c0009000000fdfffe3ffff9407344704000041910120100000000efc10609c26e900e11f61a947802127c0009000000fdfffe3ffff9407453304000041910120100000008ffffe501ca508100000000000a508100000404804000000849f00024000003f7fff8ffffe501ca508100000fe501ca508100000fffe501ca51c10000000024000003f7fff8ffffe501ca51c1", MessageType.BSM)); + // DecodedMessage bsm = manager.decode(new EncodedMessage("00145144ad0b7947c2ed9ad2748035a4e8ff880000000fd2229199307d7d07d0b17fff05407d12720038c000fe72c107b001ea88fffeb4002127c0009000000fdfffe3ffff9407344704000041910120100000000efc10609c26e900e11f61a947802127c0009000000fdfffe3ffff9407453304000041910120100000008ffffe501ca508100000000000a508100000404804000000849f00024000003f7fff8ffffe501ca508100000fe501ca508100000fffe501ca51c10000000024000003f7fff8ffffe501ca51c1", MessageType.BSM)); - System.out.println(bsm); - + // System.out.println(bsm); + // System.out.println("Fully Decoded BSM" + bsm.getBsm()); // OdeBsmData decodeTest; @@ -47,6 +53,14 @@ public static void main(String[] args) { // } } + @Bean + public void test(){ + System.out.println("Running Decoder"); + DecodedMessage map = manager.decode(new EncodedMessage("0012839338023000205e96094d40df4c2ca626c8516e02dc3c2010640000000289e01c009f603f42e88039900000000a41107b027d80fd0a4200c6400000002973021c09f603de0c16029200000080002a8a008d027d98fee805404fb0e1085f60588200028096021200000080002aa0007d027d98fe9802e04fb1200c214456228000a02b1240005022c03240000020000d56b40bc04fb35ff655e2c09f623fb81c835fec0db240a0a2bff4aebf82c660000804b0089000000800025670034013ecd7fb9578e027d9aff883c4e050515ffa567a41635000040258024800000400012b8f81f409f663fac094013ecd7fc83ddb02829affa480bc04fb02c6e0000804b09c5000000200035ea98a9604f60da6c7c113d505c35ffe941d409f65c05034c050500c9880004409bc800000006d2bd3cec813c40cde062c1fd400000200008791ea3db3cf380a009f666f05005813d80ffe0a0588c00040092106a00000000bc75cac009f66db54c04a813d80a100801241ed40000000078ebae3b6da7a008809e2050904008811f100000000bc72389009f60eca8002049c400000002f1b2ca3027d93a71fa813ec204bc400000002f1b2b34027b0397608880cd10000000039b8e1a51036820505080d51000000003a7461ed1036760505080dd1000000003b2f62311006260505160bca00000080002b785e2a80a0a6c028de728145037f1f9e456488000202b2540001022c1894000001000057058c5b81414d806dbcd4028a18f4df23a050502c8d0000404b05a5000000800035b6471bc05053602431f380a2864087bdb0141458064ab0d6c00053fc013ec0b0680006012c15940000020000d6c06c6581414d807fb972028a1901d78dc050536020ec1800a0a6c039d639813d80b0780006012c1494000002000096ab8c6581414d8062be32028a1b01417e04050a360172d77009e2058440003009409c200000040006b3486a480a0a1cab7134c8117dcc02879b018fae2c050f3601ced54809e21012720000000067fbad0007e7e84045c80000000100661580958004041c8000000019f3658401cdfa2c0d64000002000144016c02c36ddfff0282984acc1ee05052c36f0ac02828669d82da8f821480a0a10f140002c8e0001004b03190000008000519fd190c43b2e0066108b08401428c342a0ce02828258a0604a6be959aee0e6050502c920001004b02d90000008000459fa164404fb30a8580a00a14619c306701414c32ce10e02829659081f814141029030164b000080200",MessageType.MAP)); + System.out.println(map); + + } + // @Bean // public WebMvcConfigurer corsConfigurer() { // return new WebMvcConfigurer() { diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/BsmDecoder.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/BsmDecoder.java index 59662b38..3cde5ece 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/BsmDecoder.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/BsmDecoder.java @@ -1,5 +1,7 @@ package us.dot.its.jpo.ode.api.asn1; +import org.springframework.stereotype.Component; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -28,6 +30,8 @@ import us.dot.its.jpo.ode.util.XmlUtils; import us.dot.its.jpo.ode.util.XmlUtils.XmlUtilsException; + +@Component public class BsmDecoder implements Decoder { @@ -44,8 +48,8 @@ public DecodedMessage decode(EncodedMessage message) { String xml = xmlUtils.toXml(data); // Send String through ASN.1 Decoder to get Decoded XML Data - // String decodedXml = DecoderManager.decodeXmlWithAcm(xml); - String decodedXml = mockDecodeXmlWithAcm(xml); + String decodedXml = DecoderManager.decodeXmlWithAcm(xml); + // String decodedXml = mockDecodeXmlWithAcm(xml); // Convert to Ode Json OdeBsmData bsm = getAsOdeJson(decodedXml); diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/DecoderManager.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/DecoderManager.java index 632230cb..1439307b 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/DecoderManager.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/DecoderManager.java @@ -7,6 +7,7 @@ import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.ArrayUtils; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.io.*; import java.nio.charset.StandardCharsets; @@ -27,6 +28,24 @@ public class DecoderManager { public static final int HEADER_MINIMUM_SIZE = 20; public static final int bufferSize = 2048; + @Autowired + public BsmDecoder bsmDecoder; + + @Autowired + public MapDecoder mapDecoder; + + @Autowired + public SpatDecoder spatDecoder; + + @Autowired + public SrmDecoder srmDecoder; + + @Autowired + public SsmDecoder ssmDecoder; + + @Autowired + public TimDecoder timDecoder; + // public static DecodedMessage decode(String inputAsn1){ // // Identify Message Type and Cut off any extra characters @@ -64,7 +83,7 @@ public class DecoderManager { // return null; // } - public static DecodedMessage decode(EncodedMessage message){ + public DecodedMessage decode(EncodedMessage message){ String payload = removeHeader(message.getAsn1Message(), message.getType()); message.setAsn1Message(payload); @@ -75,15 +94,20 @@ public static DecodedMessage decode(EncodedMessage message){ decoder = new BsmDecoder(); } else if(message.getType() == MessageType.MAP){ - decoder = new MapDecoder(); + // decoder = new MapDecoder(); + decoder = mapDecoder; }else if(message.getType() == MessageType.SPAT){ - decoder = new SpatDecoder(); + // decoder = new SpatDecoder(); + decoder = spatDecoder; }else if(message.getType() == MessageType.SRM){ - decoder = new SrmDecoder(); + // decoder = new SrmDecoder(); + decoder = srmDecoder; }else if(message.getType() == MessageType.SSM){ - decoder = new SsmDecoder(); + // decoder = new SsmDecoder(); + decoder = ssmDecoder; }else if(message.getType() == MessageType.TIM){ - decoder = new TimDecoder(); + // decoder = new TimDecoder(); + decoder = timDecoder; }else{ return new DecodedMessage(payload, message.getType(), "No Valid Decoder found for Message Type"); } diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/MapDecoder.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/MapDecoder.java index e17ff29a..3a99afa6 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/MapDecoder.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/MapDecoder.java @@ -1,11 +1,23 @@ package us.dot.its.jpo.ode.api.asn1; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import us.dot.its.jpo.ode.api.models.messages.MapDecodedMessage; +import us.dot.its.jpo.geojsonconverter.converter.map.MapProcessedJsonConverter; +import us.dot.its.jpo.geojsonconverter.partitioner.RsuIntersectionKey; +import us.dot.its.jpo.geojsonconverter.pojos.geojson.LineString; +import us.dot.its.jpo.geojsonconverter.pojos.geojson.connectinglanes.ConnectingLanesFeatureCollection; +import us.dot.its.jpo.geojsonconverter.pojos.geojson.map.MapFeatureCollection; +import us.dot.its.jpo.geojsonconverter.pojos.geojson.map.MapSharedProperties; +import us.dot.its.jpo.geojsonconverter.pojos.geojson.map.ProcessedMap; +import us.dot.its.jpo.geojsonconverter.validator.JsonValidatorResult; +import us.dot.its.jpo.geojsonconverter.validator.MapJsonValidator; import us.dot.its.jpo.ode.api.models.messages.DecodedMessage; import us.dot.its.jpo.ode.api.models.messages.EncodedMessage; import us.dot.its.jpo.ode.context.AppContext; @@ -24,13 +36,19 @@ import us.dot.its.jpo.ode.model.OdeMsgPayload; import us.dot.its.jpo.ode.model.ReceivedMessageDetails; import us.dot.its.jpo.ode.model.RxSource; +import us.dot.its.jpo.ode.plugin.j2735.J2735IntersectionGeometry; import us.dot.its.jpo.ode.plugin.j2735.builders.MAPBuilder; import us.dot.its.jpo.ode.util.JsonUtils; import us.dot.its.jpo.ode.util.XmlUtils; import us.dot.its.jpo.ode.util.XmlUtils.XmlUtilsException; +@Component public class MapDecoder implements Decoder { + @Autowired + MapJsonValidator mapJsonValidator; + + public MapProcessedJsonConverter converter = new MapProcessedJsonConverter(); @Override public DecodedMessage decode(EncodedMessage message) { @@ -46,14 +64,20 @@ public DecodedMessage decode(EncodedMessage message) { // Send String through ASN.1 Decoder to get Decoded XML Data String decodedXml = DecoderManager.decodeXmlWithAcm(xml); - + // String decodedXml = mockMapDecode(); // Convert to Ode Json OdeMapData map = getAsOdeJson(decodedXml); - // build output data structure - DecodedMessage decodedMessage = new MapDecodedMessage(null, map, message.getAsn1Message(), ""); - return decodedMessage; + try{ + ProcessedMap processedMap = createProcessedMap(map); + // build output data structure + return new MapDecodedMessage(processedMap, map, message.getAsn1Message(), ""); + }catch (Exception e) { + return new MapDecodedMessage(null, map, message.getAsn1Message(), e.getMessage()); + } + + } catch (JsonProcessingException e) { e.printStackTrace(); @@ -121,4 +145,31 @@ public OdeMapData getAsOdeJson(String consumedData) throws XmlUtilsException { return new OdeMapData(metadata, payload); } + public ProcessedMap createProcessedMap(OdeMapData odeMap){ + JsonValidatorResult validationResults = mapJsonValidator.validate(odeMap.toString()); + OdeMapMetadata mapMetadata = (OdeMapMetadata)odeMap.getMetadata(); + OdeMapPayload mapPayload = (OdeMapPayload)odeMap.getPayload(); + J2735IntersectionGeometry intersection = mapPayload.getMap().getIntersections().getIntersections().get(0); + + + + MapSharedProperties sharedProps = converter.createProperties(mapPayload, mapMetadata, intersection, validationResults); + MapFeatureCollection mapFeatureCollection = converter.createFeatureCollection(intersection); + ConnectingLanesFeatureCollection connectingLanesFeatureCollection = converter.createConnectingLanesFeatureCollection(mapMetadata, intersection); + + ProcessedMap processedMapObject = new ProcessedMap(); + processedMapObject.setMapFeatureCollection(mapFeatureCollection); + processedMapObject.setConnectingLanesFeatureCollection(connectingLanesFeatureCollection); + processedMapObject.setProperties(sharedProps); + + var key = new RsuIntersectionKey(); + key.setRsuId(mapMetadata.getOriginIp()); + key.setIntersectionReferenceID(intersection.getId()); + return processedMapObject; + } + + public String mockMapDecode(){ + return "mapTxsuccessunsecuredDataMessageFrameUPERus.dot.its.jpo.ode.model.OdeAsn1Payloadaf11b4ee-e2eb-48ef-82c6-97172b6cda7a10002024-05-10T15:57:46.531370277Z60falseRSU172.18.0.1MessageFrame1800121100395952649-10509141221677036621100000000000000000001000000000002225808-1010517-161-602769112-106142-180-306636-12-203804-7191000000000004131100000000000000000001000000000002222515-102933-82-106259-85-403416-98-206867-39-302657-55-10425967-1034810181000000000004111100000000000000000000010000000002215121810451-223-701852-108-1028001000000000141100000000000000000000100000000002213-124-10718720-4054140-101298229120100000000007162010000000000000000002267-1019-10365752010000000000000000002267-662-10338072010000000000000000002294-1463-103652712401000000000000000000-952-18980-37713401000000000000000000-1440-18987-390000710310000000000000000000100000000000705-1919-1-7515-40-41-527210-17-1133717-59381000-7-7261000000000006193100000000000000000001000000000001090-1930-109-4764-10-20-6402-20-34-7953-1016-1094020271000000000006111310000000000000000000010000000000350-1925-32-6698-30156-2616180100000000001183100000000000000000000010000000001557-1956-10-101-1956-5-7393-40-121-16367001000000000115510000000000000000000100000000000-2405-101810-93392630-11895-3850-88141361000000000008116510000000000000000000100000000000-2337-65610-122291550-11362-3730-6537151000000000008117510000000000000000000010000000000-2338-28410-8892-1830-11959-250-1564-180260100000000003114510000000000000000000001000000000-2401-138010-19947880-10108-713001000000000118601000000000000000000-226447920-580-1419601000000000000000000-225779620-518020601000000000000000000-2236122920-484023710000000000000000000100000000000-9141903202251228830409634-20328035-3000-728121000000000002122710000000000000000000100000000000-12211914209210475308911582-20487912-30131000000000002121710000000000000000000001000000000-1649193520163182102159912010248108511803-30194161-2020001000000000124710000000000000000000010000000000-5351926202845821088379310-14318661060100000000005125710000000000000000000010000000000-21319132014459410-9122381050100000000005126801000000000000000000724190210-735210278010000000000000000001105188810-733210288010000000000000000001591187410-1434607"; + } + } diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/SpatDecoder.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/SpatDecoder.java index 85ebbf26..85038ad1 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/SpatDecoder.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/SpatDecoder.java @@ -1,10 +1,18 @@ package us.dot.its.jpo.ode.api.asn1; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import us.dot.its.jpo.ode.api.models.messages.SpatDecodedMessage; +import us.dot.its.jpo.geojsonconverter.converter.spat.SpatProcessedJsonConverter; +import us.dot.its.jpo.geojsonconverter.partitioner.RsuIntersectionKey; +import us.dot.its.jpo.geojsonconverter.pojos.spat.ProcessedSpat; +import us.dot.its.jpo.geojsonconverter.validator.JsonValidatorResult; +import us.dot.its.jpo.geojsonconverter.validator.SpatJsonValidator; import us.dot.its.jpo.ode.api.models.messages.DecodedMessage; import us.dot.its.jpo.ode.api.models.messages.EncodedMessage; import us.dot.its.jpo.ode.context.AppContext; @@ -24,13 +32,21 @@ import us.dot.its.jpo.ode.model.OdeMsgPayload; import us.dot.its.jpo.ode.model.ReceivedMessageDetails; import us.dot.its.jpo.ode.model.RxSource; +import us.dot.its.jpo.ode.plugin.j2735.J2735IntersectionState; import us.dot.its.jpo.ode.plugin.j2735.builders.SPATBuilder; import us.dot.its.jpo.ode.util.JsonUtils; import us.dot.its.jpo.ode.util.XmlUtils; import us.dot.its.jpo.ode.util.XmlUtils.XmlUtilsException; + +@Component public class SpatDecoder implements Decoder { + @Autowired + SpatJsonValidator spatJsonValidator; + + public SpatProcessedJsonConverter converter = new SpatProcessedJsonConverter(); + @Override public DecodedMessage decode(EncodedMessage message) { @@ -52,8 +68,12 @@ public DecodedMessage decode(EncodedMessage message) { OdeSpatData spat = getAsOdeJson(decodedXml); // build output data structure - DecodedMessage decodedMessage = new SpatDecodedMessage(null, spat, message.getAsn1Message(), ""); - return decodedMessage; + try{ + ProcessedSpat processedSpat = createProcessedSpat(spat); + return new SpatDecodedMessage(processedSpat, spat, message.getAsn1Message(), ""); + } catch(Exception e) { + return new SpatDecodedMessage(null, spat, message.getAsn1Message(), e.getMessage()); + } } catch (JsonProcessingException e) { e.printStackTrace(); @@ -132,4 +152,24 @@ public OdeSpatData getAsOdeJson(String consumedData) throws XmlUtilsException { return new OdeSpatData(metadata, payload); } + public ProcessedSpat createProcessedSpat(OdeSpatData odeSpat){ + + JsonValidatorResult validationResults = spatJsonValidator.validate(odeSpat.toString()); + OdeSpatData rawValue = new OdeSpatData(); + rawValue.setMetadata(odeSpat.getMetadata()); + OdeSpatMetadata spatMetadata = (OdeSpatMetadata)rawValue.getMetadata(); + + rawValue.setPayload(odeSpat.getPayload()); + OdeSpatPayload spatPayload = (OdeSpatPayload)rawValue.getPayload(); + J2735IntersectionState intersectionState = spatPayload.getSpat().getIntersectionStateList().getIntersectionStatelist().get(0); + + ProcessedSpat processedSpat = converter.createProcessedSpat(intersectionState, spatMetadata, validationResults); + + var key = new RsuIntersectionKey(); + key.setRsuId(spatMetadata.getOriginIp()); + key.setIntersectionReferenceID(intersectionState.getId()); + return processedSpat; + + } + } diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/SrmDecoder.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/SrmDecoder.java index 0892478a..a627fc7b 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/SrmDecoder.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/SrmDecoder.java @@ -1,5 +1,7 @@ package us.dot.its.jpo.ode.api.asn1; +import org.springframework.stereotype.Component; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -26,6 +28,8 @@ import us.dot.its.jpo.ode.util.XmlUtils; import us.dot.its.jpo.ode.util.XmlUtils.XmlUtilsException; + +@Component public class SrmDecoder implements Decoder { diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/SsmDecoder.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/SsmDecoder.java index 7b95c406..434abbc0 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/SsmDecoder.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/SsmDecoder.java @@ -1,5 +1,7 @@ package us.dot.its.jpo.ode.api.asn1; +import org.springframework.stereotype.Component; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -26,6 +28,7 @@ import us.dot.its.jpo.ode.util.XmlUtils; import us.dot.its.jpo.ode.util.XmlUtils.XmlUtilsException; +@Component public class SsmDecoder implements Decoder { diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/TimDecoder.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/TimDecoder.java index 23ab2b38..abe1b339 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/TimDecoder.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/TimDecoder.java @@ -1,5 +1,7 @@ package us.dot.its.jpo.ode.api.asn1; +import org.springframework.stereotype.Component; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -23,6 +25,7 @@ import us.dot.its.jpo.ode.util.XmlUtils; import us.dot.its.jpo.ode.util.XmlUtils.XmlUtilsException; +@Component public class TimDecoder implements Decoder { diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/controllers/DecoderController.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/controllers/DecoderController.java index 1bb0ddb3..9942f6cc 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/controllers/DecoderController.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/controllers/DecoderController.java @@ -40,6 +40,8 @@ public class DecoderController { @Autowired ConflictMonitorApiProperties props; + @Autowired DecoderManager decoderManager; + // private StringPublisher bsmPublisher; public String getCurrentTime() { @@ -119,7 +121,7 @@ public String getCurrentTime() { } } - DecodedMessage decodedMessage = DecoderManager.decode(encodedMessage); + DecodedMessage decodedMessage = decoderManager.decode(encodedMessage); return ResponseEntity.status(HttpStatus.OK).contentType(MediaType.TEXT_PLAIN) .body(decodedMessage.toString()); diff --git a/api/jpo-conflictvisualizer-api/test.pdf b/api/jpo-conflictvisualizer-api/test.pdf new file mode 100644 index 0000000000000000000000000000000000000000..fe65b00f931080daef43efab2438c04e3cceea57 GIT binary patch literal 227419 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

l*kkaJ(*Z?Bf@|&4etgkW{uM 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 Date: Fri, 10 May 2024 15:36:43 -0600 Subject: [PATCH 12/29] Fixed Tim Decoding --- .../jpo/ode/api/ConflictApiApplication.java | 32 +------------ .../its/jpo/ode/api/asn1/DecoderManager.java | 6 +-- .../dot/its/jpo/ode/api/asn1/TimDecoder.java | 46 ++++++------------- .../api/controllers/DecoderController.java | 1 - .../models/messages/TimDecodedMessage.java | 8 ++-- .../jpo/ode/mockdata/MockTimGenerator.java | 8 ++-- 6 files changed, 25 insertions(+), 76 deletions(-) diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/ConflictApiApplication.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/ConflictApiApplication.java index 5a54a1c8..7089d06f 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/ConflictApiApplication.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/ConflictApiApplication.java @@ -7,12 +7,7 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc; import us.dot.its.jpo.ode.api.asn1.DecoderManager; -import us.dot.its.jpo.ode.api.models.MessageType; -import us.dot.its.jpo.ode.api.models.messages.BsmDecodedMessage; -import us.dot.its.jpo.ode.api.models.messages.DecodedMessage; -import us.dot.its.jpo.ode.api.models.messages.EncodedMessage; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.scheduling.annotation.EnableScheduling; @@ -33,32 +28,7 @@ public static void main(String[] args) { SpringApplication.run(ConflictApiApplication.class, args); System.out.println("Started Conflict Monitor API"); System.out.println("Conflict Monitor API docs page found here: http://localhost:8081/swagger-ui/index.html"); - System.out.println("Startup Complete"); - - // DecodedMessage bsm = manager.decode(new EncodedMessage("00145144ad0b7947c2ed9ad2748035a4e8ff880000000fd2229199307d7d07d0b17fff05407d12720038c000fe72c107b001ea88fffeb4002127c0009000000fdfffe3ffff9407344704000041910120100000000efc10609c26e900e11f61a947802127c0009000000fdfffe3ffff9407453304000041910120100000008ffffe501ca508100000000000a508100000404804000000849f00024000003f7fff8ffffe501ca508100000fe501ca508100000fffe501ca51c10000000024000003f7fff8ffffe501ca51c1", MessageType.BSM)); - - // System.out.println(bsm); - - - - - // System.out.println("Fully Decoded BSM" + bsm.getBsm()); - // OdeBsmData decodeTest; - // try { - // decodeTest = OdeBsmDataCreatorHelper.createOdeBsmData("RVbsmTxsuccessRVunsecuredDataMessageFrameUPERus.dot.its.jpo.ode.model.OdeAsn1Payloadf48cd9fa-cf32-4542-9cfd-4ff0542d56b010002024-05-07T18:45:29.796137Z60false10.165.5.230MessageFrame201052123484129609403192909-111704926414321404081005421320812720012001-12700111132012800-14434616781268-3227713981122522407912025454581096553565535101051"); - // System.out.println(decodeTest); - // } catch (XmlUtilsException e) { - // // TODO Auto-generated catch block - // e.printStackTrace(); - // } - } - - @Bean - public void test(){ - System.out.println("Running Decoder"); - DecodedMessage map = manager.decode(new EncodedMessage("0012839338023000205e96094d40df4c2ca626c8516e02dc3c2010640000000289e01c009f603f42e88039900000000a41107b027d80fd0a4200c6400000002973021c09f603de0c16029200000080002a8a008d027d98fee805404fb0e1085f60588200028096021200000080002aa0007d027d98fe9802e04fb1200c214456228000a02b1240005022c03240000020000d56b40bc04fb35ff655e2c09f623fb81c835fec0db240a0a2bff4aebf82c660000804b0089000000800025670034013ecd7fb9578e027d9aff883c4e050515ffa567a41635000040258024800000400012b8f81f409f663fac094013ecd7fc83ddb02829affa480bc04fb02c6e0000804b09c5000000200035ea98a9604f60da6c7c113d505c35ffe941d409f65c05034c050500c9880004409bc800000006d2bd3cec813c40cde062c1fd400000200008791ea3db3cf380a009f666f05005813d80ffe0a0588c00040092106a00000000bc75cac009f66db54c04a813d80a100801241ed40000000078ebae3b6da7a008809e2050904008811f100000000bc72389009f60eca8002049c400000002f1b2ca3027d93a71fa813ec204bc400000002f1b2b34027b0397608880cd10000000039b8e1a51036820505080d51000000003a7461ed1036760505080dd1000000003b2f62311006260505160bca00000080002b785e2a80a0a6c028de728145037f1f9e456488000202b2540001022c1894000001000057058c5b81414d806dbcd4028a18f4df23a050502c8d0000404b05a5000000800035b6471bc05053602431f380a2864087bdb0141458064ab0d6c00053fc013ec0b0680006012c15940000020000d6c06c6581414d807fb972028a1901d78dc050536020ec1800a0a6c039d639813d80b0780006012c1494000002000096ab8c6581414d8062be32028a1b01417e04050a360172d77009e2058440003009409c200000040006b3486a480a0a1cab7134c8117dcc02879b018fae2c050f3601ced54809e21012720000000067fbad0007e7e84045c80000000100661580958004041c8000000019f3658401cdfa2c0d64000002000144016c02c36ddfff0282984acc1ee05052c36f0ac02828669d82da8f821480a0a10f140002c8e0001004b03190000008000519fd190c43b2e0066108b08401428c342a0ce02828258a0604a6be959aee0e6050502c920001004b02d90000008000459fa164404fb30a8580a00a14619c306701414c32ce10e02829659081f814141029030164b000080200",MessageType.MAP)); - System.out.println(map); - + System.out.println("Startup Complete"); } // @Bean diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/DecoderManager.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/DecoderManager.java index 1439307b..8ac7603c 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/DecoderManager.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/DecoderManager.java @@ -94,20 +94,16 @@ public DecodedMessage decode(EncodedMessage message){ decoder = new BsmDecoder(); } else if(message.getType() == MessageType.MAP){ - // decoder = new MapDecoder(); decoder = mapDecoder; }else if(message.getType() == MessageType.SPAT){ - // decoder = new SpatDecoder(); decoder = spatDecoder; }else if(message.getType() == MessageType.SRM){ - // decoder = new SrmDecoder(); decoder = srmDecoder; }else if(message.getType() == MessageType.SSM){ - // decoder = new SsmDecoder(); decoder = ssmDecoder; }else if(message.getType() == MessageType.TIM){ - // decoder = new TimDecoder(); decoder = timDecoder; + // return new DecodedMessage(payload, message.getType(), "Ode Does not support ODE Serialization / Deserialization"); }else{ return new DecodedMessage(payload, message.getType(), "No Valid Decoder found for Message Type"); } diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/TimDecoder.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/TimDecoder.java index abe1b339..926ddc72 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/TimDecoder.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/TimDecoder.java @@ -3,8 +3,12 @@ import org.springframework.stereotype.Component; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; + +import net.minidev.json.JSONObject; import us.dot.its.jpo.ode.api.models.messages.TimDecodedMessage; import us.dot.its.jpo.ode.api.models.messages.DecodedMessage; import us.dot.its.jpo.ode.api.models.messages.EncodedMessage; @@ -16,6 +20,7 @@ import us.dot.its.jpo.ode.model.OdeTimData; import us.dot.its.jpo.ode.model.OdeTimMetadata; import us.dot.its.jpo.ode.model.OdeTimPayload; +import us.dot.its.jpo.ode.plugin.j2735.builders.TravelerMessageFromHumanToAsnConverter; import us.dot.its.jpo.ode.model.OdeData; import us.dot.its.jpo.ode.model.OdeHexByteArray; import us.dot.its.jpo.ode.model.OdeLogMetadata.RecordType; @@ -27,7 +32,7 @@ @Component public class TimDecoder implements Decoder { - + ObjectMapper objectMapper = new ObjectMapper(); @Override public DecodedMessage decode(EncodedMessage message) { @@ -43,10 +48,10 @@ public DecodedMessage decode(EncodedMessage message) { // Send String through ASN.1 Decoder to get Decoded XML Data String decodedXml = DecoderManager.decodeXmlWithAcm(xml); - + // String decodedXml = mockTim(); // Convert to Ode Json - OdeTimData tim = getAsOdeJson(decodedXml); + ObjectNode tim = XmlUtils.toObjectNode(decodedXml); // build output data structure DecodedMessage decodedMessage = new TimDecodedMessage(tim, message.getAsn1Message(), ""); @@ -84,37 +89,14 @@ public OdeData getAsOdeData(String encodedData) { @Override public OdeTimData getAsOdeJson(String consumedData) throws XmlUtilsException { - ObjectNode consumed = XmlUtils.toObjectNode(consumedData); - JsonNode metadataNode = consumed.findValue(AppContext.METADATA_STRING); - if (metadataNode instanceof ObjectNode) { - ObjectNode object = (ObjectNode) metadataNode; - object.remove(AppContext.ENCODINGS_STRING); - } + // There is no proper deserilizer for TIM data into the ODE tim format. This function is not used here. - JsonNode payloadNode = consumed.findValue("TravelerInformationMessage"); - if (payloadNode instanceof ObjectNode) { - ObjectNode object = (ObjectNode) payloadNode; - object.remove(AppContext.ENCODINGS_STRING); - } - - OdeTimMetadata metadata = (OdeTimMetadata) JsonUtils.fromJson( - metadataNode.toString(), OdeTimMetadata.class); - - OdeTimPayload payload = (OdeTimPayload) JsonUtils.fromJson( - payloadNode.toString(), OdeTimPayload.class); - - /* - * ODE-755 and ODE-765 Starting with schemaVersion=5 receivedMessageDetails - * will be present in BSM metadata. None should be present in prior versions. - */ - if (metadata.getSchemaVersion() <= 4) { - metadata.setReceivedMessageDetails(null); - } - - // OdeTimPayload payload = new OdeTimPayload( - // TimBuilder.genericTim(consumed.findValue("TravelerInformationMessage"))); - return new OdeTimData(metadata, payload); + return null; + } + + public String mockTim(){ + return "timMsgunsecuredDataMessageFrameUPERus.dot.its.jpo.ode.model.OdeAsn1Payloadd768dfa6-9b37-4a98-b16c-c0c68e5989c510002024-05-10T17:43:23.093127553Z60RSUfalse172.18.0.1MessageFrame3114490890000000000000BBC2Bnull1411269876-104726956311111111111111112018448260144051westbound_I-80_366.0_365.0_RSU-10.145.1.100_RW_445600411269876-10472695633270000000000000100000-1047287423411264686-1047305390411260104-1047323629411256185-1047342080411252886-1047360706411250207-1047379480411248201-1047398354411246839-1047417290411246133-1047436246411245796-1047455202411245470-10474741594112451731177713579null"; } } diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/controllers/DecoderController.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/controllers/DecoderController.java index 9942f6cc..0c3a551c 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/controllers/DecoderController.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/controllers/DecoderController.java @@ -16,7 +16,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import us.dot.its.jpo.ode.api.models.MessageType; -import us.dot.its.jpo.ode.api.models.TypePayload; import us.dot.its.jpo.ode.api.models.messages.DecodedMessage; import us.dot.its.jpo.ode.api.models.messages.EncodedMessage; import us.dot.its.jpo.ode.mockdata.MockDecodedMessageGenerator; diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/TimDecodedMessage.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/TimDecodedMessage.java index 252aad92..a49adca3 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/TimDecodedMessage.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/models/messages/TimDecodedMessage.java @@ -1,16 +1,18 @@ package us.dot.its.jpo.ode.api.models.messages; import us.dot.its.jpo.ode.api.models.MessageType; -import us.dot.its.jpo.ode.model.OdeTimData; + +import com.fasterxml.jackson.databind.node.ObjectNode; + import lombok.Getter; import lombok.Setter; @Setter @Getter public class TimDecodedMessage extends DecodedMessage{ - public OdeTimData tim; + public ObjectNode tim; - public TimDecodedMessage(OdeTimData tim, String asn1Text, String decodeErrors) { + public TimDecodedMessage(ObjectNode tim, String asn1Text, String decodeErrors) { super(asn1Text, MessageType.TIM, decodeErrors); this.tim = tim; } diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockTimGenerator.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockTimGenerator.java index d9a7dcf1..9aabcca1 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockTimGenerator.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/mockdata/MockTimGenerator.java @@ -6,21 +6,21 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import us.dot.its.jpo.ode.model.OdeTimData; public class MockTimGenerator { static String timString = "{\"metadata\":{\"securityResultCode\":\"\",\"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\":\"02fd3137-f282-4f06-b735-66c5e2e963be\",\"bundleSize\":\"1\",\"bundleId\":\"0\"},\"logFileName\":\"\",\"odeReceivedAt\":\"2024-05-09T15:33:57.386279283Z\",\"originIp\":\"172.18.0.1\"},\"payload\":{\"data\":{\"MessageFrame\":{\"messageId\":\"31\",\"value\":{\"TravelerInformation\":{\"timeStamp\":\"449089\",\"packetID\":\"0000000000000BBC2B\",\"urlB\":\"null\",\"dataFrames\":{\"TravelerDataFrame\":{\"regions\":{\"GeographicalPath\":{\"closedPath\":{\"false\":\"\"},\"anchor\":{\"lat\":\"411269876\",\"long\":\"-1047269563\"},\"name\":\"westbound_I-80_366.0_365.0_RSU-10.145.1.100_RW_4456\",\"laneWidth\":\"32700\",\"directionality\":{\"both\":\"\"},\"description\":{\"path\":{\"offset\":{\"xy\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"node-LatLon\":{\"lon\":\"-1047287423\",\"lat\":\"411264686\"}}},{\"delta\":{\"node-LatLon\":{\"lon\":\"-1047305390\",\"lat\":\"411260104\"}}},{\"delta\":{\"node-LatLon\":{\"lon\":\"-1047323629\",\"lat\":\"411256185\"}}},{\"delta\":{\"node-LatLon\":{\"lon\":\"-1047342080\",\"lat\":\"411252886\"}}},{\"delta\":{\"node-LatLon\":{\"lon\":\"-1047360706\",\"lat\":\"411250207\"}}},{\"delta\":{\"node-LatLon\":{\"lon\":\"-1047379480\",\"lat\":\"411248201\"}}},{\"delta\":{\"node-LatLon\":{\"lon\":\"-1047398354\",\"lat\":\"411246839\"}}},{\"delta\":{\"node-LatLon\":{\"lon\":\"-1047417290\",\"lat\":\"411246133\"}}},{\"delta\":{\"node-LatLon\":{\"lon\":\"-1047436246\",\"lat\":\"411245796\"}}},{\"delta\":{\"node-LatLon\":{\"lon\":\"-1047455202\",\"lat\":\"411245470\"}}},{\"delta\":{\"node-LatLon\":{\"lon\":\"-1047474159\",\"lat\":\"411245173\"}}}]}}},\"scale\":\"0\"}},\"id\":{\"id\":\"0\",\"region\":\"0\"},\"direction\":\"0000000000010000\"}},\"duratonTime\":\"1440\",\"sspMsgRights1\":\"1\",\"sspMsgRights2\":\"1\",\"startYear\":\"2018\",\"msgId\":{\"roadSignID\":{\"viewAngle\":\"1111111111111111\",\"mutcdCode\":{\"warning\":\"\"},\"position\":{\"lat\":\"411269876\",\"long\":\"-1047269563\"}}},\"priority\":\"5\",\"content\":{\"advisory\":{\"SEQUENCE\":[{\"item\":{\"itis\":\"777\"}},{\"item\":{\"itis\":\"13579\"}}]}},\"url\":\"null\",\"sspTimRights\":\"1\",\"sspLocationRights\":\"1\",\"frameType\":{\"advisory\":\"\"},\"startTime\":\"448260\"}},\"msgCnt\":\"1\"}}}},\"dataType\":\"TravelerInformation\"}}"; - public static List getJsonTims(){ + public static List getJsonTims(){ ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(new JavaTimeModule()); - ArrayList tims = new ArrayList<>(); + ArrayList tims = new ArrayList<>(); try { - OdeTimData tim = objectMapper.readValue(timString, OdeTimData.class); + ObjectNode tim = objectMapper.readValue(timString, ObjectNode.class); tims.add(tim); } catch (JsonMappingException e) { System.out.println("A Json Mapping Exception Occurred while trying to get data from mocked tim."); From 34cef1326bb17551ae1533432374546d227449ef Mon Sep 17 00:00:00 2001 From: John-Wiens Date: Fri, 10 May 2024 17:17:24 -0600 Subject: [PATCH 13/29] Updating Dockerfile to use Amazon Corretto --- api/Dockerfile | 186 +++++++++--------- api/confluent.repo | 13 ++ .../dot/its/jpo/ode/api/asn1/TimDecoder.java | 7 - 3 files changed, 107 insertions(+), 99 deletions(-) create mode 100644 api/confluent.repo diff --git a/api/Dockerfile b/api/Dockerfile index b4fecab6..1a0aa137 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -65,23 +65,18 @@ # === BUILDER IMAGE for ACM === -FROM alpine:3.12 as builder +# FROM alpine:3.12 as builder +FROM amazonlinux:2023 as builder USER root WORKDIR /asn1_codec +## add build dependencies +RUN yum install -y cmake g++ make bash automake libtool autoconf flex bison -# add build dependencies -RUN apk add --upgrade --no-cache --virtual .build-deps \ - cmake \ - g++ \ - make \ - bash \ - automake \ - libtool \ - autoconf \ - librdkafka \ - librdkafka-dev \ - flex \ - bison +# Install librdkafka from Confluent repo +RUN rpm --import https://packages.confluent.io/rpm/7.6/archive.key +COPY ./confluent.repo /etc/yum.repos.d +RUN yum clean all +RUN yum install -y librdkafka-devel # Install pugixml ADD ./asn1_codec/pugixml /asn1_codec/pugixml @@ -118,7 +113,7 @@ ADD ./asn1_codec/data /asn1_codec/data RUN mkdir -p /build && cd /build && cmake /asn1_codec && make -# === Build image for Java === +# # === Build image for Java === FROM maven:3.8-eclipse-temurin-21-alpine as jbuilder WORKDIR /home @@ -158,12 +153,13 @@ RUN mvn clean install -DskipTests WORKDIR /home/jpo-conflictvisualizer-api RUN mvn clean package -DskipTests -# ENTRYPOINT ["tail", "-f", "/dev/null"] +# # ENTRYPOINT ["tail", "-f", "/dev/null"] -# === RUNTIME IMAGE for Java and ACM === -# Using Alpine 3.12 because ACM does not compile or run on the latest Alpine (3.19) -FROM alpine:3.12 +# # === RUNTIME IMAGE for Java and ACM === +# # Use Amazon Corretto Java on Amazon Linum 2023 to match the codec build env +# # FROM alpine:3.12 +FROM amazoncorretto:21-al2023 WORKDIR /home @@ -172,91 +168,97 @@ COPY --from=jbuilder /home/jpo-conflictvisualizer-api/src/main/resources/applica COPY --from=jbuilder /home/jpo-conflictvisualizer-api/src/main/resources/logback.xml /home COPY --from=jbuilder /home/jpo-conflictvisualizer-api/target/jpo-conflictvisualizer-api-0.0.1-SNAPSHOT.jar /home -# Copy asn1_codec executable and test files +# # Copy asn1_codec executable and test files USER root WORKDIR /asn1_codec -# add runtime dependencies -RUN apk add --upgrade --no-cache \ - bash \ - librdkafka \ - librdkafka-dev -# copy the built files from the builder + +# # add runtime dependencies +RUN yum install -y bash + + +# Install librdkafka from Confluent repo +RUN rpm --import https://packages.confluent.io/rpm/7.6/archive.key +COPY ./confluent.repo /etc/yum.repos.d +RUN yum clean all +RUN yum install -y librdkafka-devel + +# # copy the built files from the builder COPY --from=builder /asn1_codec /asn1_codec COPY --from=builder /build /build -# Add test data. This changes frequently so keep it low in the file. +# # Add test data. This changes frequently so keep it low in the file. ADD ./asn1_codec/docker-test /asn1_codec/docker-test -# Put workdir back to Java home +# # Put workdir back to Java home WORKDIR /home -# Install Java 21, not available from apk in Alpine 3.12 -# Dockerfile for eclipse-temurin:21-jre-alpine copied from: -# https://github.com/adoptium/containers/blob/057e5aa7581e96b8a2334290e750b329d62abfdf/21/jre/alpine/Dockerfile - -ENV JAVA_HOME /opt/java/openjdk -ENV PATH $JAVA_HOME/bin:$PATH - -# Default to UTF-8 file.encoding -ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' LC_ALL='en_US.UTF-8' - -RUN set -eux; \ - apk add --no-cache \ - # java.lang.UnsatisfiedLinkError: libfontmanager.so: libfreetype.so.6: cannot open shared object file: No such file or directory - # java.lang.NoClassDefFoundError: Could not initialize class sun.awt.X11FontManager - # https://github.com/docker-library/openjdk/pull/235#issuecomment-424466077 - fontconfig ttf-dejavu \ - # utilities for keeping Alpine and OpenJDK CA certificates in sync - # https://github.com/adoptium/containers/issues/293 - ca-certificates p11-kit-trust \ - # locales ensures proper character encoding and locale-specific behaviors using en_US.UTF-8 - musl-locales musl-locales-lang \ - tzdata \ - ; \ - rm -rf /var/cache/apk/* - -ENV JAVA_VERSION jdk-21.0.3+9 - -RUN set -eux; \ - ARCH="$(apk --print-arch)"; \ - case "${ARCH}" in \ - aarch64|arm64) \ - ESUM='54e8618da373258654fe788d509f087d3612de9e080eb6831601069dbc8a4b2b'; \ - BINARY_URL='https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.3%2B9/OpenJDK21U-jre_aarch64_alpine-linux_hotspot_21.0.3_9.tar.gz'; \ - ;; \ - amd64|x86_64) \ - ESUM='b3e7170deab11a7089fe8e14f9f398424fd86db085f745dad212f6cfc4121df6'; \ - BINARY_URL='https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.3%2B9/OpenJDK21U-jre_x64_alpine-linux_hotspot_21.0.3_9.tar.gz'; \ - ;; \ - *) \ - echo "Unsupported arch: ${ARCH}"; \ - exit 1; \ - ;; \ - esac; \ - wget -O /tmp/openjdk.tar.gz ${BINARY_URL}; \ - echo "${ESUM} */tmp/openjdk.tar.gz" | sha256sum -c -; \ - mkdir -p "$JAVA_HOME"; \ - tar --extract \ - --file /tmp/openjdk.tar.gz \ - --directory "$JAVA_HOME" \ - --strip-components 1 \ - --no-same-owner \ - ; \ - rm -f /tmp/openjdk.tar.gz ${JAVA_HOME}/lib/src.zip; - -RUN set -eux; \ - echo "Verifying install ..."; \ - echo "java --version"; java --version; \ - echo "Complete." - - - -# ENTRYPOINT ["java", "-jar", "/home/asn1-codec-java.jar"] -# #COPY cert.crt /home/cert.crt -# #RUN keytool -import -trustcacerts -keystore /opt/java/openjdk/lib/security/cacerts -storepass changeit -noprompt -alias mycert -file cert.crt +# # Install Java 21, not available from apk in Alpine 3.12 +# # Dockerfile for eclipse-temurin:21-jre-alpine copied from: +# # https://github.com/adoptium/containers/blob/057e5aa7581e96b8a2334290e750b329d62abfdf/21/jre/alpine/Dockerfile + +# ENV JAVA_HOME /opt/java/openjdk +# ENV PATH $JAVA_HOME/bin:$PATH + +# # Default to UTF-8 file.encoding +# ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' LC_ALL='en_US.UTF-8' + +# RUN set -eux; \ +# apk add --no-cache \ +# # java.lang.UnsatisfiedLinkError: libfontmanager.so: libfreetype.so.6: cannot open shared object file: No such file or directory +# # java.lang.NoClassDefFoundError: Could not initialize class sun.awt.X11FontManager +# # https://github.com/docker-library/openjdk/pull/235#issuecomment-424466077 +# fontconfig ttf-dejavu \ +# # utilities for keeping Alpine and OpenJDK CA certificates in sync +# # https://github.com/adoptium/containers/issues/293 +# ca-certificates p11-kit-trust \ +# # locales ensures proper character encoding and locale-specific behaviors using en_US.UTF-8 +# musl-locales musl-locales-lang \ +# tzdata \ +# ; \ +# rm -rf /var/cache/apk/* + +# ENV JAVA_VERSION jdk-21.0.3+9 + +# RUN set -eux; \ +# ARCH="$(apk --print-arch)"; \ +# case "${ARCH}" in \ +# aarch64|arm64) \ +# ESUM='54e8618da373258654fe788d509f087d3612de9e080eb6831601069dbc8a4b2b'; \ +# BINARY_URL='https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.3%2B9/OpenJDK21U-jre_aarch64_alpine-linux_hotspot_21.0.3_9.tar.gz'; \ +# ;; \ +# amd64|x86_64) \ +# ESUM='b3e7170deab11a7089fe8e14f9f398424fd86db085f745dad212f6cfc4121df6'; \ +# BINARY_URL='https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.3%2B9/OpenJDK21U-jre_x64_alpine-linux_hotspot_21.0.3_9.tar.gz'; \ +# ;; \ +# *) \ +# echo "Unsupported arch: ${ARCH}"; \ +# exit 1; \ +# ;; \ +# esac; \ +# wget -O /tmp/openjdk.tar.gz ${BINARY_URL}; \ +# echo "${ESUM} */tmp/openjdk.tar.gz" | sha256sum -c -; \ +# mkdir -p "$JAVA_HOME"; \ +# tar --extract \ +# --file /tmp/openjdk.tar.gz \ +# --directory "$JAVA_HOME" \ +# --strip-components 1 \ +# --no-same-owner \ +# ; \ +# rm -f /tmp/openjdk.tar.gz ${JAVA_HOME}/lib/src.zip; + +# RUN set -eux; \ +# echo "Verifying install ..."; \ +# echo "java --version"; java --version; \ +# echo "Complete." + + + +# # ENTRYPOINT ["java", "-jar", "/home/asn1-codec-java.jar"] +# # #COPY cert.crt /home/cert.crt +# # #RUN keytool -import -trustcacerts -keystore /opt/java/openjdk/lib/security/cacerts -storepass changeit -noprompt -alias mycert -file cert.crt ENTRYPOINT ["java", \ "-Djava.rmi.server.hostname=$DOCKER_HOST_IP", \ diff --git a/api/confluent.repo b/api/confluent.repo new file mode 100644 index 00000000..7140bd56 --- /dev/null +++ b/api/confluent.repo @@ -0,0 +1,13 @@ +[Confluent] +name=Confluent repository +baseurl=https://packages.confluent.io/rpm/7.6 +gpgcheck=1 +gpgkey=https://packages.confluent.io/rpm/7.6/archive.key +enabled=1 + +[Confluent-Clients] +name=Confluent Clients repository +baseurl=https://packages.confluent.io/clients/rpm/centos/9/x86_64 +gpgcheck=1 +gpgkey=https://packages.confluent.io/clients/rpm/archive.key +enabled=1 diff --git a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/TimDecoder.java b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/TimDecoder.java index 926ddc72..123cafcd 100644 --- a/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/TimDecoder.java +++ b/api/jpo-conflictvisualizer-api/src/main/java/us/dot/its/jpo/ode/api/asn1/TimDecoder.java @@ -3,30 +3,23 @@ import org.springframework.stereotype.Component; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; -import net.minidev.json.JSONObject; import us.dot.its.jpo.ode.api.models.messages.TimDecodedMessage; import us.dot.its.jpo.ode.api.models.messages.DecodedMessage; import us.dot.its.jpo.ode.api.models.messages.EncodedMessage; -import us.dot.its.jpo.ode.context.AppContext; import us.dot.its.jpo.ode.model.Asn1Encoding; import us.dot.its.jpo.ode.model.Asn1Encoding.EncodingRule; import us.dot.its.jpo.ode.model.OdeAsn1Data; import us.dot.its.jpo.ode.model.OdeAsn1Payload; import us.dot.its.jpo.ode.model.OdeTimData; import us.dot.its.jpo.ode.model.OdeTimMetadata; -import us.dot.its.jpo.ode.model.OdeTimPayload; -import us.dot.its.jpo.ode.plugin.j2735.builders.TravelerMessageFromHumanToAsnConverter; import us.dot.its.jpo.ode.model.OdeData; import us.dot.its.jpo.ode.model.OdeHexByteArray; import us.dot.its.jpo.ode.model.OdeLogMetadata.RecordType; import us.dot.its.jpo.ode.model.OdeMsgMetadata.GeneratedBy; import us.dot.its.jpo.ode.model.OdeMsgPayload; -import us.dot.its.jpo.ode.util.JsonUtils; import us.dot.its.jpo.ode.util.XmlUtils; import us.dot.its.jpo.ode.util.XmlUtils.XmlUtilsException; From c697dc8610128e65a4ba0a1f2efa4b6d8f38a5f8 Mon Sep 17 00:00:00 2001 From: John-Wiens Date: Mon, 13 May 2024 12:46:54 -0600 Subject: [PATCH 14/29] Adding Submodule References --- .gitmodules | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index bcd46a3a..dc15a748 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,8 @@ [submodule "api/jpo-conflictmonitor"] path = api/jpo-conflictmonitor url = https://github.com/usdot-jpo-ode/jpo-conflictmonitor - branch = develop \ No newline at end of file + branch = develop +[submodule "api/asn1_codec"] + path = api/jpo-conflictmonitor + url = https://github.com:iyourshaw/asn1_codec + branch = develop From b5cfbc3fda28bffb679b57821480f9d1d2b5098a Mon Sep 17 00:00:00 2001 From: John-Wiens Date: Mon, 13 May 2024 12:57:12 -0600 Subject: [PATCH 15/29] Removing Asn1_Codec --- .gitmodules | 5 +---- api/asn1_codec | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) delete mode 160000 api/asn1_codec diff --git a/.gitmodules b/.gitmodules index dc15a748..7d1e28a6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,7 +2,4 @@ path = api/jpo-conflictmonitor url = https://github.com/usdot-jpo-ode/jpo-conflictmonitor branch = develop -[submodule "api/asn1_codec"] - path = api/jpo-conflictmonitor - url = https://github.com:iyourshaw/asn1_codec - branch = develop + diff --git a/api/asn1_codec b/api/asn1_codec deleted file mode 160000 index 422bcab4..00000000 --- a/api/asn1_codec +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 422bcab41e119dafa39eb1ca812b68e06ca4dbbe From fc04a4bf9bd34e77c27ae559790a0504dc2eea27 Mon Sep 17 00:00:00 2001 From: John-Wiens Date: Mon, 13 May 2024 14:09:37 -0600 Subject: [PATCH 16/29] Adding asn1_codec as submodule --- .gitmodules | 3 +++ api/asn1_codec | 1 + 2 files changed, 4 insertions(+) create mode 160000 api/asn1_codec diff --git a/.gitmodules b/.gitmodules index 7d1e28a6..f93547e7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,3 +3,6 @@ url = https://github.com/usdot-jpo-ode/jpo-conflictmonitor branch = develop +[submodule "api/asn1_codec"] + path = api/asn1_codec + url = https://github.com/iyourshaw/asn1_codec.git diff --git a/api/asn1_codec b/api/asn1_codec new file mode 160000 index 00000000..422bcab4 --- /dev/null +++ b/api/asn1_codec @@ -0,0 +1 @@ +Subproject commit 422bcab41e119dafa39eb1ca812b68e06ca4dbbe From bdbecf738c60296b3bb816c8651a3ab5b1d00c34 Mon Sep 17 00:00:00 2001 From: John-Wiens Date: Tue, 14 May 2024 16:34:15 -0600 Subject: [PATCH 17/29] Adding Schemas to Classpath --- .../src/main/resources/application.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/api/jpo-conflictvisualizer-api/src/main/resources/application.yaml b/api/jpo-conflictvisualizer-api/src/main/resources/application.yaml index 2168f18d..970ecf16 100644 --- a/api/jpo-conflictvisualizer-api/src/main/resources/application.yaml +++ b/api/jpo-conflictvisualizer-api/src/main/resources/application.yaml @@ -17,6 +17,11 @@ spring.kafka.bootstrap-servers: localhost:9092 logging.level.org.apache.kafka: INFO logging.level: INFO + +schema.map: classpath:schemas/map.schema.json +schema.spat: classpath:schemas/spat.schema.json + + kafka.topics: autoCreateTopics: false numPartitions: 1 @@ -27,4 +32,4 @@ kafka.topics: retentionMs: 300000 - name: topic.ProcessedMap cleanupPolicy: delete - retentionMs: 300000 \ No newline at end of file + retentionMs: 300000 From 7886405dd27a9e652ffb29a127fd02b8892b64da Mon Sep 17 00:00:00 2001 From: John-Wiens Date: Wed, 15 May 2024 15:08:43 -0600 Subject: [PATCH 18/29] Adding Decoder Unit tests --- .../dot/its/jpo/ode/api/AssessmentTests.java | 1 - .../dot/its/jpo/ode/api/MockKeyCloakAuth.java | 1 - .../events/BsmEventRepositoryImplTest.java | 1 - .../ode/api/decoderTests/BsmDecoderTests.java | 57 +++++++++++++++ .../ode/api/decoderTests/MapDecoderTests.java | 69 +++++++++++++++++++ .../api/decoderTests/SpatDecoderTests.java | 69 +++++++++++++++++++ .../ode/api/decoderTests/SrmDecoderTests.java | 58 ++++++++++++++++ .../ode/api/decoderTests/SsmDecoderTests.java | 58 ++++++++++++++++ .../ode/api/decoderTests/TimDecoderTests.java | 45 ++++++++++++ 9 files changed, 356 insertions(+), 3 deletions(-) create mode 100644 api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/decoderTests/BsmDecoderTests.java create mode 100644 api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/decoderTests/MapDecoderTests.java create mode 100644 api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/decoderTests/SpatDecoderTests.java create mode 100644 api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/decoderTests/SrmDecoderTests.java create mode 100644 api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/decoderTests/SsmDecoderTests.java create mode 100644 api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/decoderTests/TimDecoderTests.java diff --git a/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/AssessmentTests.java b/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/AssessmentTests.java index a11b7393..467d0d33 100644 --- a/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/AssessmentTests.java +++ b/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/AssessmentTests.java @@ -19,7 +19,6 @@ import us.dot.its.jpo.conflictmonitor.monitor.models.assessments.ConnectionOfTravelAssessment; import us.dot.its.jpo.conflictmonitor.monitor.models.assessments.LaneDirectionOfTravelAssessment; -import us.dot.its.jpo.conflictmonitor.monitor.models.assessments.SignalStateAssessment; import us.dot.its.jpo.conflictmonitor.monitor.models.assessments.StopLinePassageAssessment; import us.dot.its.jpo.conflictmonitor.monitor.models.assessments.StopLineStopAssessment; import us.dot.its.jpo.ode.api.accessors.assessments.ConnectionOfTravelAssessment.ConnectionOfTravelAssessmentRepository; diff --git a/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/MockKeyCloakAuth.java b/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/MockKeyCloakAuth.java index ad88cfdf..562ef721 100644 --- a/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/MockKeyCloakAuth.java +++ b/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/MockKeyCloakAuth.java @@ -6,7 +6,6 @@ import java.util.Set; -import org.mockito.Mockito; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; diff --git a/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/accessorTests/events/BsmEventRepositoryImplTest.java b/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/accessorTests/events/BsmEventRepositoryImplTest.java index c8356f36..99a8fc36 100644 --- a/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/accessorTests/events/BsmEventRepositoryImplTest.java +++ b/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/accessorTests/events/BsmEventRepositoryImplTest.java @@ -14,7 +14,6 @@ import static org.assertj.core.api.Assertions.assertThat; import java.util.ArrayList; -import java.util.Date; import java.util.List; import org.bson.Document; diff --git a/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/decoderTests/BsmDecoderTests.java b/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/decoderTests/BsmDecoderTests.java new file mode 100644 index 00000000..4aa14783 --- /dev/null +++ b/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/decoderTests/BsmDecoderTests.java @@ -0,0 +1,57 @@ +package us.dot.its.jpo.ode.api.decoderTests; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import us.dot.its.jpo.ode.api.asn1.BsmDecoder; +import us.dot.its.jpo.ode.api.models.messages.BsmDecodedMessage; +import us.dot.its.jpo.ode.mockdata.MockDecodedMessageGenerator; +import us.dot.its.jpo.ode.model.OdeBsmData; +import us.dot.its.jpo.ode.model.OdeData; +import us.dot.its.jpo.ode.model.OdeMsgMetadata; + +import us.dot.its.jpo.ode.util.XmlUtils.XmlUtilsException; + + +@SpringBootTest +@RunWith(SpringRunner.class) +public class BsmDecoderTests { + + + @Autowired + BsmDecoder bsmDecoder; + + private String odeBsmDataReference = "{\"metadata\":{\"bsmSource\":\"RV\",\"recordType\":\"bsmTx\",\"securityResultCode\":\"success\",\"receivedMessageDetails\":{\"locationData\":{},\"rxSource\":\"RV\"},\"encodings\":[{\"elementName\":\"unsecuredData\",\"elementType\":\"MessageFrame\",\"encodingRule\":\"UPER\"}],\"payloadType\":\"us.dot.its.jpo.ode.model.OdeAsn1Payload\",\"serialId\":{\"streamId\":\"fc430f29-b761-4a2c-90fb-dc4c9f5d4e9c\",\"bundleSize\":1,\"bundleId\":0,\"recordId\":0,\"serialNumber\":0},\"odeReceivedAt\":\"2024-05-14T23:01:21.516531700Z\",\"schemaVersion\":6,\"maxDurationTime\":0,\"sanitized\":false},\"payload\":{\"dataType\":\"us.dot.its.jpo.ode.model.OdeHexByteArray\",\"data\":{\"bytes\":\"00145144ad0b7947c2ed9ad2748035a4e8ff880000000fd2229199307d7d07d0b17fff05407d12720038c000fe72c107b001ea88fffeb4002127c0009000000fdfffe3ffff9407344704000041910120100000000efc10609c26e900e11f61a947802127c0009000000fdfffe3ffff9407453304000041910120100000008ffffe501ca508100000000000a508100000404804000000849f00024000003f7fff8ffffe501ca508100000fe501ca508100000fffe501ca51c10000000024000003f7fff8ffffe501ca51c1\"}}}"; + private String odeBsmDecodedXmlReference = "RVbsmTxsuccessRVunsecuredDataMessageFrameUPERus.dot.its.jpo.ode.model.OdeAsn1Payload8829c539-e684-40b7-a786-692acd3f897a10002024-05-08T20:47:53.830130272Z60falseMessageFrame2018B42DE51F299800000810013156448-100500000002505900-79521090627893276718020065535655351010527101001000000000000000"; + private String odeBsmDecodedDataReference = "{\"metadata\":{\"bsmSource\":\"RV\",\"logFileName\":\"\",\"recordType\":\"bsmTx\",\"securityResultCode\":\"success\",\"receivedMessageDetails\":{\"locationData\":{\"latitude\":\"\",\"longitude\":\"\",\"elevation\":\"\",\"speed\":\"\",\"heading\":\"\"},\"rxSource\":\"RV\"},\"payloadType\":\"us.dot.its.jpo.ode.model.OdeBsmPayload\",\"serialId\":{\"streamId\":\"8829c539-e684-40b7-a786-692acd3f897a\",\"bundleSize\":1,\"bundleId\":0,\"recordId\":0,\"serialNumber\":0},\"odeReceivedAt\":\"2024-05-08T20:47:53.830130272Z\",\"schemaVersion\":6,\"maxDurationTime\":0,\"recordGeneratedAt\":\"\",\"sanitized\":false,\"odePacketID\":\"\",\"odeTimStartDateTime\":\"\",\"originIp\":\"\"},\"payload\":{\"data\":{\"coreData\":{\"msgCnt\":18,\"id\":\"B42DE51F\",\"secMark\":2998,\"position\":{\"latitude\":0E-7,\"longitude\":0E-7,\"elevation\":0.0},\"accelSet\":{\"accelLat\":0.00,\"accelLong\":0.00,\"accelVert\":1.00,\"accelYaw\":0.00},\"accuracy\":{\"semiMajor\":0.00,\"semiMinor\":0.00,\"orientation\":44.4953079900},\"transmission\":\"FORWARDGEARS\",\"speed\":26.30,\"heading\":80.6000,\"angle\":-1.5,\"brakes\":{\"wheelBrakes\":{\"leftFront\":false,\"rightFront\":false,\"unavailable\":true,\"leftRear\":false,\"rightRear\":false},\"traction\":\"on\",\"abs\":\"on\",\"scs\":\"on\",\"brakeBoost\":\"unavailable\",\"auxBrakes\":\"unavailable\"},\"size\":{\"width\":250,\"length\":590}},\"partII\":[]},\"dataType\":\"us.dot.its.jpo.ode.plugin.j2735.J2735Bsm\"}}"; + + + @Test + public void testBsmGetAsOdeData() { + + BsmDecodedMessage bsm = MockDecodedMessageGenerator.getBsmDecodedMessage(); + OdeData data = bsmDecoder.getAsOdeData(bsm.getAsn1Text()); + + OdeMsgMetadata metadata = data.getMetadata(); + + + // Copy over fields that might be different + metadata.setOdeReceivedAt("2024-05-14T23:01:21.516531700Z"); + metadata.setSerialId(metadata.getSerialId().setStreamId("fc430f29-b761-4a2c-90fb-dc4c9f5d4e9c")); + + assertEquals(data.toJson(), odeBsmDataReference); + + } + + @Test + public void testBsmGetAsOdeJson() throws XmlUtilsException{ + OdeBsmData bsm = bsmDecoder.getAsOdeJson(odeBsmDecodedXmlReference); + assertEquals(bsm.toJson(), odeBsmDecodedDataReference); + } + +} diff --git a/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/decoderTests/MapDecoderTests.java b/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/decoderTests/MapDecoderTests.java new file mode 100644 index 00000000..b7a4647d --- /dev/null +++ b/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/decoderTests/MapDecoderTests.java @@ -0,0 +1,69 @@ +package us.dot.its.jpo.ode.api.decoderTests; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import us.dot.its.jpo.ode.api.asn1.MapDecoder; +import us.dot.its.jpo.geojsonconverter.pojos.geojson.LineString; +import us.dot.its.jpo.geojsonconverter.pojos.geojson.map.ProcessedMap; +import us.dot.its.jpo.ode.api.models.messages.MapDecodedMessage; +import us.dot.its.jpo.ode.mockdata.MockDecodedMessageGenerator; +import us.dot.its.jpo.ode.model.OdeMapData; +import us.dot.its.jpo.ode.model.OdeData; +import us.dot.its.jpo.ode.model.OdeMsgMetadata; + +import us.dot.its.jpo.ode.util.XmlUtils.XmlUtilsException; + + +@SpringBootTest +@RunWith(SpringRunner.class) +public class MapDecoderTests { + + + @Autowired + MapDecoder mapDecoder; + + private String odeMapDataReference = "{\"metadata\":{\"recordType\":\"mapTx\",\"securityResultCode\":\"success\",\"encodings\":[{\"elementName\":\"unsecuredData\",\"elementType\":\"MessageFrame\",\"encodingRule\":\"UPER\"}],\"payloadType\":\"us.dot.its.jpo.ode.model.OdeAsn1Payload\",\"serialId\":{\"streamId\":\"fc430f29-b761-4a2c-90fb-dc4c9f5d4e9c\",\"bundleSize\":1,\"bundleId\":0,\"recordId\":0,\"serialNumber\":0},\"odeReceivedAt\":\"2024-05-14T23:01:21.516531700Z\",\"schemaVersion\":6,\"maxDurationTime\":0,\"sanitized\":false,\"mapSource\":\"RSU\",\"originIp\":\"user-upload\"},\"payload\":{\"dataType\":\"us.dot.its.jpo.ode.model.OdeHexByteArray\",\"data\":{\"bytes\":\"0012839338023000205e96094d40df4c2ca626c8516e02dc3c2010640000000289e01c009f603f42e88039900000000a41107b027d80fd0a4200c6400000002973021c09f603de0c16029200000080002a8a008d027d98fee805404fb0e1085f60588200028096021200000080002aa0007d027d98fe9802e04fb1200c214456228000a02b1240005022c03240000020000d56b40bc04fb35ff655e2c09f623fb81c835fec0db240a0a2bff4aebf82c660000804b0089000000800025670034013ecd7fb9578e027d9aff883c4e050515ffa567a41635000040258024800000400012b8f81f409f663fac094013ecd7fc83ddb02829affa480bc04fb02c6e0000804b09c5000000200035ea98a9604f60da6c7c113d505c35ffe941d409f65c05034c050500c9880004409bc800000006d2bd3cec813c40cde062c1fd400000200008791ea3db3cf380a009f666f05005813d80ffe0a0588c00040092106a00000000bc75cac009f66db54c04a813d80a100801241ed40000000078ebae3b6da7a008809e2050904008811f100000000bc72389009f60eca8002049c400000002f1b2ca3027d93a71fa813ec204bc400000002f1b2b34027b0397608880cd10000000039b8e1a51036820505080d51000000003a7461ed1036760505080dd1000000003b2f62311006260505160bca00000080002b785e2a80a0a6c028de728145037f1f9e456488000202b2540001022c1894000001000057058c5b81414d806dbcd4028a18f4df23a050502c8d0000404b05a5000000800035b6471bc05053602431f380a2864087bdb0141458064ab0d6c00053fc013ec0b0680006012c15940000020000d6c06c6581414d807fb972028a1901d78dc050536020ec1800a0a6c039d639813d80b0780006012c1494000002000096ab8c6581414d8062be32028a1b01417e04050a360172d77009e2058440003009409c200000040006b3486a480a0a1cab7134c8117dcc02879b018fae2c050f3601ced54809e21012720000000067fbad0007e7e84045c80000000100661580958004041c8000000019f3658401cdfa2c0d64000002000144016c02c36ddfff0282984acc1ee05052c36f0ac02828669d82da8f821480a0a10f140002c8e0001004b03190000008000519fd190c43b2e0066108b08401428c342a0ce02828258a0604a6be959aee0e6050502c920001004b02d90000008000459fa164404fb30a8580a00a14619c306701414c32ce10e02829659081f814141029030164b000080200\"}}}"; + private String odeMapDecodedXmlReference = "mapTxsuccessunsecuredDataMessageFrameUPERus.dot.its.jpo.ode.model.OdeAsn1Payloadaf11b4ee-e2eb-48ef-82c6-97172b6cda7a10002024-05-10T15:57:46.531370277Z60falseRSU172.18.0.1MessageFrame1800121100395952649-10509141221677036621100000000000000000001000000000002225808-1010517-161-602769112-106142-180-306636-12-203804-7191000000000004131100000000000000000001000000000002222515-102933-82-106259-85-403416-98-206867-39-302657-55-10425967-1034810181000000000004111100000000000000000000010000000002215121810451-223-701852-108-1028001000000000141100000000000000000000100000000002213-124-10718720-4054140-101298229120100000000007162010000000000000000002267-1019-10365752010000000000000000002267-662-10338072010000000000000000002294-1463-103652712401000000000000000000-952-18980-37713401000000000000000000-1440-18987-390000710310000000000000000000100000000000705-1919-1-7515-40-41-527210-17-1133717-59381000-7-7261000000000006193100000000000000000001000000000001090-1930-109-4764-10-20-6402-20-34-7953-1016-1094020271000000000006111310000000000000000000010000000000350-1925-32-6698-30156-2616180100000000001183100000000000000000000010000000001557-1956-10-101-1956-5-7393-40-121-16367001000000000115510000000000000000000100000000000-2405-101810-93392630-11895-3850-88141361000000000008116510000000000000000000100000000000-2337-65610-122291550-11362-3730-6537151000000000008117510000000000000000000010000000000-2338-28410-8892-1830-11959-250-1564-180260100000000003114510000000000000000000001000000000-2401-138010-19947880-10108-713001000000000118601000000000000000000-226447920-580-1419601000000000000000000-225779620-518020601000000000000000000-2236122920-484023710000000000000000000100000000000-9141903202251228830409634-20328035-3000-728121000000000002122710000000000000000000100000000000-12211914209210475308911582-20487912-30131000000000002121710000000000000000000001000000000-1649193520163182102159912010248108511803-30194161-2020001000000000124710000000000000000000010000000000-5351926202845821088379310-14318661060100000000005125710000000000000000000010000000000-21319132014459410-9122381050100000000005126801000000000000000000724190210-735210278010000000000000000001105188810-733210288010000000000000000001591187410-1434607"; + private String odeMapDecodedDataReference = "{\"metadata\":{\"logFileName\":\"\",\"recordType\":\"mapTx\",\"securityResultCode\":\"success\",\"receivedMessageDetails\":{\"rxSource\":\"NA\"},\"payloadType\":\"us.dot.its.jpo.ode.model.OdeMapPayload\",\"serialId\":{\"streamId\":\"af11b4ee-e2eb-48ef-82c6-97172b6cda7a\",\"bundleSize\":1,\"bundleId\":0,\"recordId\":0,\"serialNumber\":0},\"odeReceivedAt\":\"2024-05-10T15:57:46.531370277Z\",\"schemaVersion\":6,\"maxDurationTime\":0,\"recordGeneratedAt\":\"\",\"sanitized\":false,\"odePacketID\":\"\",\"odeTimStartDateTime\":\"\",\"mapSource\":\"RSU\",\"originIp\":\"172.18.0.1\"},\"payload\":{\"data\":{\"msgIssueRevision\":0,\"layerType\":\"intersectionData\",\"layerID\":0,\"intersections\":{\"intersectionGeometry\":[{\"id\":{\"id\":12110},\"revision\":0,\"refPoint\":{\"latitude\":39.5952649,\"longitude\":-105.0914122,\"elevation\":1677.0},\"laneWidth\":366,\"laneSet\":{\"GenericLane\":[{\"laneID\":2,\"ingressApproach\":1,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":2225,\"y\":808}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":10517,\"y\":-161}},\"attributes\":{\"dElevation\":-60}},{\"delta\":{\"nodeXY6\":{\"x\":2769,\"y\":112}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":6142,\"y\":-180}},\"attributes\":{\"dElevation\":-30}},{\"delta\":{\"nodeXY6\":{\"x\":6636,\"y\":-12}},\"attributes\":{\"dElevation\":-20}},{\"delta\":{\"nodeXY6\":{\"x\":3804,\"y\":-7}}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":19,\"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\":4,\"connectionID\":1}]}},{\"laneID\":3,\"ingressApproach\":1,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":2222,\"y\":515}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":2933,\"y\":-82}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":6259,\"y\":-85}},\"attributes\":{\"dElevation\":-40}},{\"delta\":{\"nodeXY6\":{\"x\":3416,\"y\":-98}},\"attributes\":{\"dElevation\":-20}},{\"delta\":{\"nodeXY6\":{\"x\":6867,\"y\":-39}},\"attributes\":{\"dElevation\":-30}},{\"delta\":{\"nodeXY6\":{\"x\":2657,\"y\":-55}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":4259,\"y\":67}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":3481,\"y\":0}}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":18,\"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\":4,\"connectionID\":1}]}},{\"laneID\":1,\"ingressApproach\":1,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":2215,\"y\":1218}}},{\"delta\":{\"nodeXY6\":{\"x\":10451,\"y\":-223}},\"attributes\":{\"dElevation\":-70}},{\"delta\":{\"nodeXY6\":{\"x\":1852,\"y\":-108}},\"attributes\":{\"dElevation\":-10}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":28,\"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}]}},{\"laneID\":4,\"ingressApproach\":1,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":2213,\"y\":-124}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":7187,\"y\":20}},\"attributes\":{\"dElevation\":-40}},{\"delta\":{\"nodeXY6\":{\"x\":541,\"y\":40}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":1298,\"y\":229}}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":12,\"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\":7,\"connectionID\":1}]}},{\"laneID\":6,\"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}}},\"nodeList\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":2267,\"y\":-1019}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":365,\"y\":7}}}]}}},{\"laneID\":5,\"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}}},\"nodeList\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":2267,\"y\":-662}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":338,\"y\":0}}}]}}},{\"laneID\":7,\"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}}},\"nodeList\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":2294,\"y\":-1463}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":365,\"y\":27}}}]}}},{\"laneID\":12,\"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}}},\"nodeList\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-952,\"y\":-1898}}},{\"delta\":{\"nodeXY6\":{\"x\":0,\"y\":-377}}}]}}},{\"laneID\":13,\"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}}},\"nodeList\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-1440,\"y\":-1898}}},{\"delta\":{\"nodeXY6\":{\"x\":7,\"y\":-390}}},{\"delta\":{\"nodeXY6\":{\"x\":0,\"y\":0}}},{\"delta\":{\"nodeXY6\":{\"x\":0,\"y\":7}}}]}}},{\"laneID\":10,\"ingressApproach\":3,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":705,\"y\":-1919}}},{\"delta\":{\"nodeXY6\":{\"x\":-1,\"y\":-7515}},\"attributes\":{\"dElevation\":-40}},{\"delta\":{\"nodeXY6\":{\"x\":-41,\"y\":-5272}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":-17,\"y\":-11337}}},{\"delta\":{\"nodeXY6\":{\"x\":17,\"y\":-5938}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":0,\"y\":0}}},{\"delta\":{\"nodeXY6\":{\"x\":-7,\"y\":-7}}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":26,\"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}]}},{\"laneID\":9,\"ingressApproach\":3,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":1090,\"y\":-1930}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":9,\"y\":-4764}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":-20,\"y\":-6402}},\"attributes\":{\"dElevation\":-20}},{\"delta\":{\"nodeXY6\":{\"x\":-34,\"y\":-7953}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":16,\"y\":-10940}},\"attributes\":{\"dElevation\":20}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":27,\"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}]}},{\"laneID\":11,\"ingressApproach\":3,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":350,\"y\":-1925}}},{\"delta\":{\"nodeXY6\":{\"x\":-32,\"y\":-6698}},\"attributes\":{\"dElevation\":-30}},{\"delta\":{\"nodeXY6\":{\"x\":156,\"y\":-2616}}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":18,\"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\":1,\"connectionID\":1}]}},{\"laneID\":8,\"ingressApproach\":3,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":1557,\"y\":-1956}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":-101,\"y\":-1956}}},{\"delta\":{\"nodeXY6\":{\"x\":-5,\"y\":-7393}},\"attributes\":{\"dElevation\":-40}},{\"delta\":{\"nodeXY6\":{\"x\":-121,\"y\":-1636}}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":7,\"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}]}},{\"laneID\":15,\"ingressApproach\":5,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-2405,\"y\":-1018}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":-9339,\"y\":26}},\"attributes\":{\"dElevation\":30}},{\"delta\":{\"nodeXY6\":{\"x\":-11895,\"y\":-38}},\"attributes\":{\"dElevation\":50}},{\"delta\":{\"nodeXY6\":{\"x\":-8814,\"y\":13}}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":6,\"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\":8,\"connectionID\":1}]}},{\"laneID\":16,\"ingressApproach\":5,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-2337,\"y\":-656}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":-12229,\"y\":15}},\"attributes\":{\"dElevation\":50}},{\"delta\":{\"nodeXY6\":{\"x\":-11362,\"y\":-37}},\"attributes\":{\"dElevation\":30}},{\"delta\":{\"nodeXY6\":{\"x\":-6537,\"y\":1}}}]}},\"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}},\"signalGroup\":8,\"connectionID\":1}]}},{\"laneID\":17,\"ingressApproach\":5,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-2338,\"y\":-284}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":-8892,\"y\":-18}},\"attributes\":{\"dElevation\":30}},{\"delta\":{\"nodeXY6\":{\"x\":-11959,\"y\":-2}},\"attributes\":{\"dElevation\":50}},{\"delta\":{\"nodeXY6\":{\"x\":-1564,\"y\":-180}}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":26,\"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\":3,\"connectionID\":1}]}},{\"laneID\":14,\"ingressApproach\":5,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-2401,\"y\":-1380}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":-19947,\"y\":8}},\"attributes\":{\"dElevation\":80}},{\"delta\":{\"nodeXY6\":{\"x\":-10108,\"y\":-7}}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":13,\"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}]}},{\"laneID\":18,\"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}}},\"nodeList\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-2264,\"y\":479}},\"attributes\":{\"dElevation\":20}},{\"delta\":{\"nodeXY6\":{\"x\":-580,\"y\":-14}}}]}}},{\"laneID\":19,\"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}}},\"nodeList\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-2257,\"y\":796}},\"attributes\":{\"dElevation\":20}},{\"delta\":{\"nodeXY6\":{\"x\":-518,\"y\":0}}}]}}},{\"laneID\":20,\"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}}},\"nodeList\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-2236,\"y\":1229}},\"attributes\":{\"dElevation\":20}},{\"delta\":{\"nodeXY6\":{\"x\":-484,\"y\":0}}}]}}},{\"laneID\":23,\"ingressApproach\":7,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-914,\"y\":1903}},\"attributes\":{\"dElevation\":20}},{\"delta\":{\"nodeXY6\":{\"x\":225,\"y\":12288}},\"attributes\":{\"dElevation\":30}},{\"delta\":{\"nodeXY6\":{\"x\":40,\"y\":9634}},\"attributes\":{\"dElevation\":-20}},{\"delta\":{\"nodeXY6\":{\"x\":32,\"y\":8035}},\"attributes\":{\"dElevation\":-30}},{\"delta\":{\"nodeXY6\":{\"x\":0,\"y\":0}}},{\"delta\":{\"nodeXY6\":{\"x\":-7,\"y\":28}}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":12,\"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}]}},{\"laneID\":22,\"ingressApproach\":7,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-1221,\"y\":1914}},\"attributes\":{\"dElevation\":20}},{\"delta\":{\"nodeXY6\":{\"x\":92,\"y\":10475}},\"attributes\":{\"dElevation\":30}},{\"delta\":{\"nodeXY6\":{\"x\":89,\"y\":11582}},\"attributes\":{\"dElevation\":-20}},{\"delta\":{\"nodeXY6\":{\"x\":48,\"y\":7912}},\"attributes\":{\"dElevation\":-30}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":13,\"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}]}},{\"laneID\":21,\"ingressApproach\":7,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-1649,\"y\":1935}},\"attributes\":{\"dElevation\":20}},{\"delta\":{\"nodeXY6\":{\"x\":16,\"y\":3182}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":21,\"y\":5991}},\"attributes\":{\"dElevation\":20}},{\"delta\":{\"nodeXY6\":{\"x\":102,\"y\":4810}}},{\"delta\":{\"nodeXY6\":{\"x\":85,\"y\":11803}},\"attributes\":{\"dElevation\":-30}},{\"delta\":{\"nodeXY6\":{\"x\":19,\"y\":4161}},\"attributes\":{\"dElevation\":-20}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":20,\"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}]}},{\"laneID\":24,\"ingressApproach\":7,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-535,\"y\":1926}},\"attributes\":{\"dElevation\":20}},{\"delta\":{\"nodeXY6\":{\"x\":28,\"y\":4582}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":88,\"y\":3793}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":-143,\"y\":1866}},\"attributes\":{\"dElevation\":10}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":6,\"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\":5,\"connectionID\":1}]}},{\"laneID\":25,\"ingressApproach\":7,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-213,\"y\":1913}},\"attributes\":{\"dElevation\":20}},{\"delta\":{\"nodeXY6\":{\"x\":14,\"y\":4594}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":-91,\"y\":2238}},\"attributes\":{\"dElevation\":10}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":5,\"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\":5,\"connectionID\":1}]}},{\"laneID\":26,\"egressApproach\":8,\"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}}},\"nodeList\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":724,\"y\":1902}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":-7,\"y\":352}},\"attributes\":{\"dElevation\":10}}]}}},{\"laneID\":27,\"egressApproach\":8,\"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}}},\"nodeList\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":1105,\"y\":1888}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":-7,\"y\":332}},\"attributes\":{\"dElevation\":10}}]}}},{\"laneID\":28,\"egressApproach\":8,\"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}}},\"nodeList\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":1591,\"y\":1874}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":-14,\"y\":346}}},{\"delta\":{\"nodeXY6\":{\"x\":0,\"y\":7}}}]}}}]}}]}},\"dataType\":\"us.dot.its.jpo.ode.plugin.j2735.J2735MAP\"}}"; + + private String processedMapDataReference = "{\"mapFeatureCollection\":{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"id\":2,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911523,39.5953376],[-105.0899239,39.5953231],[-105.0896005,39.5953332],[-105.0888831,39.595317],[-105.088108,39.5953159],[-105.0876637,39.5953153]]},\"properties\":{\"nodes\":[{\"delta\":[2225,808],\"delevation\":-10},{\"delta\":[10517,-161],\"delevation\":-60},{\"delta\":[2769,112],\"delevation\":-10},{\"delta\":[6142,-180],\"delevation\":-30},{\"delta\":[6636,-12],\"delevation\":-20},{\"delta\":[3804,-7]}],\"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\":19,\"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\":4,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":3,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911527,39.5953113],[-105.0908101,39.5953039],[-105.0900791,39.5952962],[-105.0896801,39.5952874],[-105.0888781,39.5952839],[-105.0885678,39.5952789],[-105.0880704,39.5952849],[-105.0876638,39.5952849]]},\"properties\":{\"nodes\":[{\"delta\":[2222,515],\"delevation\":-10},{\"delta\":[2933,-82],\"delevation\":-10},{\"delta\":[6259,-85],\"delevation\":-40},{\"delta\":[3416,-98],\"delevation\":-20},{\"delta\":[6867,-39],\"delevation\":-30},{\"delta\":[2657,-55],\"delevation\":-10},{\"delta\":[4259,67],\"delevation\":-10},{\"delta\":[3481,0]}],\"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\":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\":18,\"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\":4,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":1,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911535,39.5953745],[-105.0899328,39.5953544],[-105.0897165,39.5953447]]},\"properties\":{\"nodes\":[{\"delta\":[2215,1218]},{\"delta\":[10451,-223],\"delevation\":-70},{\"delta\":[1852,-108],\"delevation\":-10}],\"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\":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\":28,\"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\":4,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911537,39.5952537],[-105.0903143,39.5952555],[-105.0902511,39.5952591],[-105.0900995,39.5952797]]},\"properties\":{\"nodes\":[{\"delta\":[2213,-124],\"delevation\":-10},{\"delta\":[7187,20],\"delevation\":-40},{\"delta\":[541,40],\"delevation\":-10},{\"delta\":[1298,229]}],\"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\":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\":12,\"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\":7,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":6,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911474,39.5951732],[-105.0911048,39.5951738]]},\"properties\":{\"nodes\":[{\"delta\":[2267,-1019],\"delevation\":-10},{\"delta\":[365,7]}],\"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.0911474,39.5952053],[-105.0911079,39.5952053]]},\"properties\":{\"nodes\":[{\"delta\":[2267,-662],\"delevation\":-10},{\"delta\":[338,0]}],\"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\":7,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911443,39.5951332],[-105.0911017,39.5951356]]},\"properties\":{\"nodes\":[{\"delta\":[2294,-1463],\"delevation\":-10},{\"delta\":[365,27]}],\"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\":2,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":12,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0915234,39.5950941],[-105.0915234,39.5950602]]},\"properties\":{\"nodes\":[{\"delta\":[-952,-1898]},{\"delta\":[0,-377]}],\"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\":4,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":13,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0915804,39.5950941],[-105.0915796,39.595059],[-105.0915796,39.595059],[-105.0915796,39.5950596]]},\"properties\":{\"nodes\":[{\"delta\":[-1440,-1898]},{\"delta\":[7,-390]},{\"delta\":[0,0]},{\"delta\":[0,7]}],\"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\":4,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":10,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0913299,39.5950922],[-105.09133,39.5944158],[-105.0913348,39.5939413],[-105.0913368,39.592921],[-105.0913348,39.5923866],[-105.0913348,39.5923866],[-105.0913356,39.592386]]},\"properties\":{\"nodes\":[{\"delta\":[705,-1919]},{\"delta\":[-1,-7515],\"delevation\":-40},{\"delta\":[-41,-5272],\"delevation\":10},{\"delta\":[-17,-11337]},{\"delta\":[17,-5938],\"delevation\":10},{\"delta\":[0,0]},{\"delta\":[-7,-7]}],\"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\":0,\"ingressApproach\":3,\"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\":26,\"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\":9,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0912849,39.5950912],[-105.0912838,39.5946624],[-105.0912861,39.5940862],[-105.0912901,39.5933704],[-105.0912882,39.5923858]]},\"properties\":{\"nodes\":[{\"delta\":[1090,-1930],\"delevation\":-10},{\"delta\":[9,-4764],\"delevation\":-10},{\"delta\":[-20,-6402],\"delevation\":-20},{\"delta\":[-34,-7953],\"delevation\":-10},{\"delta\":[16,-10940],\"delevation\":20}],\"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\":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\":27,\"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.0913713,39.5950916],[-105.091375,39.5944888],[-105.0913568,39.5942534]]},\"properties\":{\"nodes\":[{\"delta\":[350,-1925]},{\"delta\":[-32,-6698],\"delevation\":-30},{\"delta\":[156,-2616]}],\"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\":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\":18,\"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\":1,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":8,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0912303,39.5950889],[-105.0912421,39.5949129],[-105.0912427,39.5942475],[-105.0912568,39.5941003]]},\"properties\":{\"nodes\":[{\"delta\":[1557,-1956],\"delevation\":-10},{\"delta\":[-101,-1956]},{\"delta\":[-5,-7393],\"delevation\":-40},{\"delta\":[-121,-1636]}],\"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\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":7,\"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\":15,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916931,39.5951733],[-105.0927839,39.5951756],[-105.0941732,39.5951722],[-105.0952027,39.5951734]]},\"properties\":{\"nodes\":[{\"delta\":[-2405,-1018],\"delevation\":10},{\"delta\":[-9339,26],\"delevation\":30},{\"delta\":[-11895,-38],\"delevation\":50},{\"delta\":[-8814,13]}],\"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\":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\":6,\"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\":8,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":16,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916852,39.5952059],[-105.0931135,39.5952073],[-105.0944406,39.595204],[-105.0952041,39.5952041]]},\"properties\":{\"nodes\":[{\"delta\":[-2337,-656],\"delevation\":10},{\"delta\":[-12229,15],\"delevation\":50},{\"delta\":[-11362,-37],\"delevation\":30},{\"delta\":[-6537,1]}],\"laneId\":16,\"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\":8,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":17,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916853,39.5952393],[-105.0927239,39.5952377],[-105.0941207,39.5952375],[-105.0943034,39.5952213]]},\"properties\":{\"nodes\":[{\"delta\":[-2338,-284],\"delevation\":10},{\"delta\":[-8892,-18],\"delevation\":30},{\"delta\":[-11959,-2],\"delevation\":50},{\"delta\":[-1564,-180]}],\"laneId\":17,\"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\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":26,\"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\":3,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":14,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916926,39.5951407],[-105.0940224,39.5951414],[-105.095203,39.5951408]]},\"properties\":{\"nodes\":[{\"delta\":[-2401,-1380],\"delevation\":10},{\"delta\":[-19947,8],\"delevation\":80},{\"delta\":[-10108,-7]}],\"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\":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\":13,\"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\":18,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916766,39.595308],[-105.0917443,39.5953067]]},\"properties\":{\"nodes\":[{\"delta\":[-2264,479],\"delevation\":20},{\"delta\":[-580,-14]}],\"laneId\":18,\"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\":19,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916758,39.5953365],[-105.0917363,39.5953365]]},\"properties\":{\"nodes\":[{\"delta\":[-2257,796],\"delevation\":20},{\"delta\":[-518,0]}],\"laneId\":19,\"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\":20,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916734,39.5953755],[-105.0917299,39.5953755]]},\"properties\":{\"nodes\":[{\"delta\":[-2236,1229],\"delevation\":20},{\"delta\":[-484,0]}],\"laneId\":20,\"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\":23,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.091519,39.5954362],[-105.0914927,39.5965421],[-105.091488,39.5974092],[-105.0914843,39.5981324],[-105.0914843,39.5981324],[-105.0914851,39.5981349]]},\"properties\":{\"nodes\":[{\"delta\":[-914,1903],\"delevation\":20},{\"delta\":[225,12288],\"delevation\":30},{\"delta\":[40,9634],\"delevation\":-20},{\"delta\":[32,8035],\"delevation\":-30},{\"delta\":[0,0]},{\"delta\":[-7,28]}],\"laneId\":23,\"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\":7,\"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\":12,\"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\":22,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0915548,39.5954372],[-105.0915441,39.59638],[-105.0915337,39.5974224],[-105.0915281,39.5981345]]},\"properties\":{\"nodes\":[{\"delta\":[-1221,1914],\"delevation\":20},{\"delta\":[92,10475],\"delevation\":30},{\"delta\":[89,11582],\"delevation\":-20},{\"delta\":[48,7912],\"delevation\":-30}],\"laneId\":22,\"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\":7,\"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\":13,\"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\":21,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916048,39.5954391],[-105.0916029,39.5957255],[-105.0916004,39.5962647],[-105.0915885,39.5966976],[-105.0915786,39.5977599],[-105.0915764,39.5981344]]},\"properties\":{\"nodes\":[{\"delta\":[-1649,1935],\"delevation\":20},{\"delta\":[16,3182],\"delevation\":10},{\"delta\":[21,5991],\"delevation\":20},{\"delta\":[102,4810]},{\"delta\":[85,11803],\"delevation\":-30},{\"delta\":[19,4161],\"delevation\":-20}],\"laneId\":21,\"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\":7,\"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\":20,\"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\":24,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0914747,39.5954382],[-105.0914714,39.5958506],[-105.0914611,39.596192],[-105.0914778,39.5963599]]},\"properties\":{\"nodes\":[{\"delta\":[-535,1926],\"delevation\":20},{\"delta\":[28,4582],\"delevation\":10},{\"delta\":[88,3793],\"delevation\":10},{\"delta\":[-143,1866],\"delevation\":10}],\"laneId\":24,\"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\":7,\"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\":6,\"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\":5,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":25,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0914371,39.5954371],[-105.0914355,39.5958506],[-105.0914461,39.596052]]},\"properties\":{\"nodes\":[{\"delta\":[-213,1913],\"delevation\":20},{\"delta\":[14,4594],\"delevation\":10},{\"delta\":[-91,2238],\"delevation\":10}],\"laneId\":25,\"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\":7,\"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\":5,\"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\":5,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":26,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0913276,39.5954361],[-105.0913284,39.5954678]]},\"properties\":{\"nodes\":[{\"delta\":[724,1902],\"delevation\":10},{\"delta\":[-7,352],\"delevation\":10}],\"laneId\":26,\"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\":8,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":27,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0912831,39.5954348],[-105.0912839,39.5954647]]},\"properties\":{\"nodes\":[{\"delta\":[1105,1888],\"delevation\":10},{\"delta\":[-7,332],\"delevation\":10}],\"laneId\":27,\"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\":8,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":28,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0912264,39.5954336],[-105.091228,39.5954647],[-105.091228,39.5954653]]},\"properties\":{\"nodes\":[{\"delta\":[1591,1874],\"delevation\":10},{\"delta\":[-14,346]},{\"delta\":[0,7]}],\"laneId\":28,\"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\":8,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}}]},\"connectingLanesFeatureCollection\":{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"id\":\"2-19\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911523,39.5953376],[-105.0916758,39.5953365]]},\"properties\":{\"signalGroupId\":4,\"ingressLaneId\":2,\"egressLaneId\":19}},{\"type\":\"Feature\",\"id\":\"3-18\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911527,39.5953113],[-105.0916766,39.595308]]},\"properties\":{\"signalGroupId\":4,\"ingressLaneId\":3,\"egressLaneId\":18}},{\"type\":\"Feature\",\"id\":\"1-28\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911535,39.5953745],[-105.0912264,39.5954336]]},\"properties\":{\"ingressLaneId\":1,\"egressLaneId\":28}},{\"type\":\"Feature\",\"id\":\"4-12\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911537,39.5952537],[-105.0915234,39.5950941]]},\"properties\":{\"signalGroupId\":7,\"ingressLaneId\":4,\"egressLaneId\":12}},{\"type\":\"Feature\",\"id\":\"10-26\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0913299,39.5950922],[-105.0913276,39.5954361]]},\"properties\":{\"signalGroupId\":6,\"ingressLaneId\":10,\"egressLaneId\":26}},{\"type\":\"Feature\",\"id\":\"9-27\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0912849,39.5950912],[-105.0912831,39.5954348]]},\"properties\":{\"signalGroupId\":6,\"ingressLaneId\":9,\"egressLaneId\":27}},{\"type\":\"Feature\",\"id\":\"11-18\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0913713,39.5950916],[-105.0916766,39.595308]]},\"properties\":{\"signalGroupId\":1,\"ingressLaneId\":11,\"egressLaneId\":18}},{\"type\":\"Feature\",\"id\":\"8-7\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0912303,39.5950889],[-105.0911443,39.5951332]]},\"properties\":{\"ingressLaneId\":8,\"egressLaneId\":7}},{\"type\":\"Feature\",\"id\":\"15-6\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916931,39.5951733],[-105.0911474,39.5951732]]},\"properties\":{\"signalGroupId\":8,\"ingressLaneId\":15,\"egressLaneId\":6}},{\"type\":\"Feature\",\"id\":\"16-5\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916852,39.5952059],[-105.0911474,39.5952053]]},\"properties\":{\"signalGroupId\":8,\"ingressLaneId\":16,\"egressLaneId\":5}},{\"type\":\"Feature\",\"id\":\"17-26\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916853,39.5952393],[-105.0913276,39.5954361]]},\"properties\":{\"signalGroupId\":3,\"ingressLaneId\":17,\"egressLaneId\":26}},{\"type\":\"Feature\",\"id\":\"14-13\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916926,39.5951407],[-105.0915804,39.5950941]]},\"properties\":{\"ingressLaneId\":14,\"egressLaneId\":13}},{\"type\":\"Feature\",\"id\":\"23-12\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.091519,39.5954362],[-105.0915234,39.5950941]]},\"properties\":{\"signalGroupId\":2,\"ingressLaneId\":23,\"egressLaneId\":12}},{\"type\":\"Feature\",\"id\":\"22-13\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0915548,39.5954372],[-105.0915804,39.5950941]]},\"properties\":{\"signalGroupId\":2,\"ingressLaneId\":22,\"egressLaneId\":13}},{\"type\":\"Feature\",\"id\":\"21-20\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916048,39.5954391],[-105.0916734,39.5953755]]},\"properties\":{\"ingressLaneId\":21,\"egressLaneId\":20}},{\"type\":\"Feature\",\"id\":\"24-6\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0914747,39.5954382],[-105.0911474,39.5951732]]},\"properties\":{\"signalGroupId\":5,\"ingressLaneId\":24,\"egressLaneId\":6}},{\"type\":\"Feature\",\"id\":\"25-5\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0914371,39.5954371],[-105.0911474,39.5952053]]},\"properties\":{\"signalGroupId\":5,\"ingressLaneId\":25,\"egressLaneId\":5}}]},\"properties\":{\"messageType\":\"MAP\",\"odeReceivedAt\":\"2024-05-10T15:57:46.531370277Z\",\"originIp\":\"172.18.0.1\",\"intersectionId\":12110,\"msgIssueRevision\":0,\"revision\":0,\"refPoint\":{\"latitude\":39.5952649,\"longitude\":-105.0914122,\"elevation\":1677.0},\"cti4501Conformant\":false,\"validationMessages\":[{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].id.region: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].id\",\"schemaPath\":\"#/$defs/J2735IntersectionReferenceID/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[2].connectsTo.connectsTo[0].signalGroup: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[2].connectsTo.connectsTo[0]\",\"schemaPath\":\"#/$defs/J2735Connection/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].maneuvers: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].connectsTo: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].maneuvers: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].connectsTo: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].maneuvers: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].connectsTo: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[7].maneuvers: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[7]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[7].connectsTo: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[7]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[8].maneuvers: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[8]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[8].connectsTo: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[8]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[12].connectsTo.connectsTo[0].signalGroup: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[12].connectsTo.connectsTo[0]\",\"schemaPath\":\"#/$defs/J2735Connection/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[16].connectsTo.connectsTo[0].signalGroup: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[16].connectsTo.connectsTo[0]\",\"schemaPath\":\"#/$defs/J2735Connection/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[17].maneuvers: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[17]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[17].connectsTo: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[17]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[18].maneuvers: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[18]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[18].connectsTo: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[18]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[19].maneuvers: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[19]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[19].connectsTo: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[19]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[22].connectsTo.connectsTo[0].signalGroup: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[22].connectsTo.connectsTo[0]\",\"schemaPath\":\"#/$defs/J2735Connection/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[25].maneuvers: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[25]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[25].connectsTo: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[25]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[26].maneuvers: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[26]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[26].connectsTo: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[26]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[27].maneuvers: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[27]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[27].connectsTo: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[27]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].speedLimits: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0]\",\"schemaPath\":\"#/$defs/J2735IntersectionGeometry/required\"}],\"laneWidth\":366,\"mapSource\":\"RSU\",\"timeStamp\":\"2024-05-10T15:57:46.531370277Z\"}}"; + + @Test + public void testMapGetAsOdeData() { + + MapDecodedMessage map = MockDecodedMessageGenerator.getMapDecodedMessage(); + OdeData data = mapDecoder.getAsOdeData(map.getAsn1Text()); + + + OdeMsgMetadata metadata = data.getMetadata(); + + + // Copy over fields that might be different + metadata.setOdeReceivedAt("2024-05-14T23:01:21.516531700Z"); + metadata.setSerialId(metadata.getSerialId().setStreamId("fc430f29-b761-4a2c-90fb-dc4c9f5d4e9c")); + + assertEquals(data.toJson(), odeMapDataReference); + + } + + @Test + public void testMapGetAsOdeJson() throws XmlUtilsException{ + OdeMapData map = mapDecoder.getAsOdeJson(odeMapDecodedXmlReference); + assertEquals(map.toJson(), odeMapDecodedDataReference); + } + + @Test + public void testCreateProcessedMap() throws XmlUtilsException{ + OdeMapData map = mapDecoder.getAsOdeJson(odeMapDecodedXmlReference); + ProcessedMap processedMap = mapDecoder.createProcessedMap(map); + assertEquals(processedMap.toString(), processedMapDataReference); + } + + +} diff --git a/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/decoderTests/SpatDecoderTests.java b/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/decoderTests/SpatDecoderTests.java new file mode 100644 index 00000000..1bc00de3 --- /dev/null +++ b/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/decoderTests/SpatDecoderTests.java @@ -0,0 +1,69 @@ +package us.dot.its.jpo.ode.api.decoderTests; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import us.dot.its.jpo.ode.api.asn1.SpatDecoder; +import us.dot.its.jpo.geojsonconverter.pojos.spat.ProcessedSpat; +import us.dot.its.jpo.ode.api.models.messages.SpatDecodedMessage; +import us.dot.its.jpo.ode.mockdata.MockDecodedMessageGenerator; +import us.dot.its.jpo.ode.model.OdeSpatData; +import us.dot.its.jpo.ode.model.OdeData; +import us.dot.its.jpo.ode.model.OdeMsgMetadata; + +import us.dot.its.jpo.ode.util.XmlUtils.XmlUtilsException; + + +@SpringBootTest +@RunWith(SpringRunner.class) +public class SpatDecoderTests { + + + @Autowired + SpatDecoder spatDecoder; + + private String odeSpatDataReference = "{\"metadata\":{\"recordType\":\"spatTx\",\"securityResultCode\":\"success\",\"receivedMessageDetails\":{\"locationData\":{},\"rxSource\":\"NA\"},\"encodings\":[{\"elementName\":\"unsecuredData\",\"elementType\":\"MessageFrame\",\"encodingRule\":\"UPER\"}],\"payloadType\":\"us.dot.its.jpo.ode.model.OdeAsn1Payload\",\"serialId\":{\"streamId\":\"f21c4bce-c04b-4ccb-a854-ca4d2f6da547\",\"bundleSize\":1,\"bundleId\":0,\"recordId\":0,\"serialNumber\":0},\"odeReceivedAt\":\"2024-05-15T19:54:27.056948Z\",\"schemaVersion\":6,\"maxDurationTime\":0,\"sanitized\":false,\"spatSource\":\"V2X\",\"originIp\":\"user-upload\",\"isCertPresent\":false},\"payload\":{\"dataType\":\"us.dot.its.jpo.ode.model.OdeHexByteArray\",\"data\":{\"bytes\":\"001338000817a780000089680500204642b342b34802021a15a955a940181190acd0acd20100868555c555c00104342aae2aae002821a155715570\"}}}"; + private String odeSpatDecodedXmlReference = "spatTxsuccessNAunsecuredDataMessageFrameUPERus.dot.its.jpo.ode.model.OdeAsn1Payloadf1438a60-a06a-42b3-a984-56e4d7153a8610002024-05-15T19:44:25.476826092Z60falseV2X172.18.0.1falseMessageFrame19121110000000000000000035176222120221214221812218162212022121821852218521218522185252185221852"; + private String odeSpatDecodedDataReference = "{\"metadata\":{\"logFileName\":\"\",\"recordType\":\"spatTx\",\"securityResultCode\":\"success\",\"receivedMessageDetails\":{\"rxSource\":\"NA\"},\"payloadType\":\"us.dot.its.jpo.ode.model.OdeSpatPayload\",\"serialId\":{\"streamId\":\"f1438a60-a06a-42b3-a984-56e4d7153a86\",\"bundleSize\":1,\"bundleId\":0,\"recordId\":0,\"serialNumber\":0},\"odeReceivedAt\":\"2024-05-15T19:44:25.476826092Z\",\"schemaVersion\":6,\"maxDurationTime\":0,\"recordGeneratedAt\":\"\",\"sanitized\":false,\"odePacketID\":\"\",\"odeTimStartDateTime\":\"\",\"spatSource\":\"V2X\",\"originIp\":\"172.18.0.1\",\"isCertPresent\":false},\"payload\":{\"data\":{\"intersectionStateList\":{\"intersectionStatelist\":[{\"id\":{\"id\":12111},\"revision\":0,\"status\":{\"failureFlash\":false,\"noValidSPATisAvailableAtThisTime\":false,\"fixedTimeOperation\":false,\"standbyOperation\":false,\"trafficDependentOperation\":false,\"manualControlIsEnabled\":false,\"off\":false,\"stopTimeIsActivated\":false,\"recentChangeInMAPassignedLanesIDsUsed\":false,\"recentMAPmessageUpdate\":false,\"failureMode\":false,\"noValidMAPisAvailableAtThisTime\":false,\"signalPriorityIsActive\":false,\"preemptIsActive\":false},\"timeStamp\":35176,\"states\":{\"movementList\":[{\"signalGroup\":2,\"state_time_speed\":{\"movementEventList\":[{\"eventState\":\"PROTECTED_MOVEMENT_ALLOWED\",\"timing\":{\"minEndTime\":22120,\"maxEndTime\":22121}}]}},{\"signalGroup\":4,\"state_time_speed\":{\"movementEventList\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":22181,\"maxEndTime\":22181}}]}},{\"signalGroup\":6,\"state_time_speed\":{\"movementEventList\":[{\"eventState\":\"PROTECTED_MOVEMENT_ALLOWED\",\"timing\":{\"minEndTime\":22120,\"maxEndTime\":22121}}]}},{\"signalGroup\":8,\"state_time_speed\":{\"movementEventList\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":21852,\"maxEndTime\":21852}}]}},{\"signalGroup\":1,\"state_time_speed\":{\"movementEventList\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":21852,\"maxEndTime\":21852}}]}},{\"signalGroup\":5,\"state_time_speed\":{\"movementEventList\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":21852,\"maxEndTime\":21852}}]}}]}}]}},\"dataType\":\"us.dot.its.jpo.ode.plugin.j2735.J2735SPAT\"}}"; + private String processedSpatDataReference = "{\"messageType\":\"SPAT\",\"odeReceivedAt\":\"2024-05-15T19:44:25.476826092Z\",\"originIp\":\"172.18.0.1\",\"intersectionId\":12111,\"cti4501Conformant\":false,\"validationMessages\":[{\"message\":\"$.payload.data.timeStamp: is missing but it is required\",\"jsonPath\":\"$.payload.data\",\"schemaPath\":\"#/$defs/J2735SPAT/required\"},{\"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\"}],\"revision\":0,\"status\":{\"manualControlIsEnabled\":false,\"stopTimeIsActivated\":false,\"failureFlash\":false,\"preemptIsActive\":false,\"signalPriorityIsActive\":false,\"fixedTimeOperation\":false,\"trafficDependentOperation\":false,\"standbyOperation\":false,\"failureMode\":false,\"off\":false,\"recentMAPmessageUpdate\":false,\"recentChangeInMAPassignedLanesIDsUsed\":false,\"noValidMAPisAvailableAtThisTime\":false,\"noValidSPATisAvailableAtThisTime\":false},\"utcTimeStamp\":\"2024-05-15T19:44:35.176Z\",\"states\":[{\"signalGroup\":2,\"stateTimeSpeed\":[{\"eventState\":\"PROTECTED_MOVEMENT_ALLOWED\",\"timing\":{\"minEndTime\":\"2024-05-15T19:36:52Z\",\"maxEndTime\":\"2024-05-15T19:36:52.1Z\"}}]},{\"signalGroup\":4,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-05-15T19:36:58.1Z\",\"maxEndTime\":\"2024-05-15T19:36:58.1Z\"}}]},{\"signalGroup\":6,\"stateTimeSpeed\":[{\"eventState\":\"PROTECTED_MOVEMENT_ALLOWED\",\"timing\":{\"minEndTime\":\"2024-05-15T19:36:52Z\",\"maxEndTime\":\"2024-05-15T19:36:52.1Z\"}}]},{\"signalGroup\":8,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-05-15T19:36:25.2Z\",\"maxEndTime\":\"2024-05-15T19:36:25.2Z\"}}]},{\"signalGroup\":1,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-05-15T19:36:25.2Z\",\"maxEndTime\":\"2024-05-15T19:36:25.2Z\"}}]},{\"signalGroup\":5,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-05-15T19:36:25.2Z\",\"maxEndTime\":\"2024-05-15T19:36:25.2Z\"}}]}]}"; + + @Test + public void testSpatGetAsOdeData() { + + SpatDecodedMessage spat = MockDecodedMessageGenerator.getSpatDecodedMessage(); + OdeData data = spatDecoder.getAsOdeData(spat.getAsn1Text()); + + + OdeMsgMetadata metadata = data.getMetadata(); + + + // Copy over fields that might be different + metadata.setOdeReceivedAt("2024-05-15T19:54:27.056948Z"); + metadata.setSerialId(metadata.getSerialId().setStreamId("f21c4bce-c04b-4ccb-a854-ca4d2f6da547")); + + assertEquals(data.toJson(), odeSpatDataReference); + + + } + + @Test + public void testSpatGetAsOdeJson() throws XmlUtilsException{ + OdeSpatData spat = spatDecoder.getAsOdeJson(odeSpatDecodedXmlReference); + System.out.println("testSpatGetAsOdeJson" + spat); + assertEquals(spat.toJson(), odeSpatDecodedDataReference); + } + + @Test + public void testCreateProcessedSpat() throws XmlUtilsException{ + OdeSpatData spat = spatDecoder.getAsOdeJson(odeSpatDecodedXmlReference); + ProcessedSpat processedSpat = spatDecoder.createProcessedSpat(spat); + assertEquals(processedSpat.toString(), processedSpatDataReference); + } + + +} diff --git a/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/decoderTests/SrmDecoderTests.java b/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/decoderTests/SrmDecoderTests.java new file mode 100644 index 00000000..3bccf818 --- /dev/null +++ b/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/decoderTests/SrmDecoderTests.java @@ -0,0 +1,58 @@ +package us.dot.its.jpo.ode.api.decoderTests; + + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import us.dot.its.jpo.ode.api.asn1.SrmDecoder; +import us.dot.its.jpo.ode.api.models.messages.SrmDecodedMessage; +import us.dot.its.jpo.ode.mockdata.MockDecodedMessageGenerator; +import us.dot.its.jpo.ode.model.OdeSrmData; +import us.dot.its.jpo.ode.model.OdeData; +import us.dot.its.jpo.ode.model.OdeMsgMetadata; + +import us.dot.its.jpo.ode.util.XmlUtils.XmlUtilsException; + + +@SpringBootTest +@RunWith(SpringRunner.class) +public class SrmDecoderTests { + + + @Autowired + SrmDecoder srmDecoder; + + private String odeSrmDataReference = "{\"metadata\":{\"recordType\":\"srmTx\",\"encodings\":[{\"elementName\":\"unsecuredData\",\"elementType\":\"MessageFrame\",\"encodingRule\":\"UPER\"}],\"payloadType\":\"us.dot.its.jpo.ode.model.OdeAsn1Payload\",\"serialId\":{\"streamId\":\"1809ff18-9251-42f0-a579-d629c1f6d60f\",\"bundleSize\":1,\"bundleId\":0,\"recordId\":0,\"serialNumber\":0},\"odeReceivedAt\":\"2024-05-15T20:02:18.273873Z\",\"schemaVersion\":6,\"maxDurationTime\":0,\"sanitized\":false,\"originIp\":\"user-upload\"},\"payload\":{\"dataType\":\"us.dot.its.jpo.ode.model.OdeHexByteArray\",\"data\":{\"bytes\":\"001d2130000010090bd341080d00855c6c0c6899853000a534f7c24cb29897694759b7c0\"}}}"; + private String odeSrmDecodedXmlReference = "srmTxunsecuredDataMessageFrameUPERus.dot.its.jpo.ode.model.OdeAsn1Payload747378da-48a6-4868-a757-8aaa7771c32910002024-05-15T19:59:40.208172634Z60false172.18.0.1RSUMessageFrame2901121094134109792366845094395904915-10509138291685414072"; + private String odeSrmDecodedDataReference = "{\"metadata\":{\"logFileName\":\"\",\"recordType\":\"srmTx\",\"receivedMessageDetails\":{\"rxSource\":\"NA\"},\"payloadType\":\"us.dot.its.jpo.ode.model.OdeSrmPayload\",\"serialId\":{\"streamId\":\"747378da-48a6-4868-a757-8aaa7771c329\",\"bundleSize\":1,\"bundleId\":0,\"recordId\":0,\"serialNumber\":0},\"odeReceivedAt\":\"2024-05-15T19:59:40.208172634Z\",\"schemaVersion\":6,\"maxDurationTime\":0,\"recordGeneratedAt\":\"\",\"sanitized\":false,\"odePacketID\":\"\",\"odeTimStartDateTime\":\"\",\"originIp\":\"172.18.0.1\",\"srmSource\":\"RSU\"},\"payload\":{\"data\":{\"second\":0,\"sequenceNumber\":1,\"requests\":{\"signalRequestPackage\":[{\"request\":{\"id\":{\"id\":12109},\"requestID\":4,\"requestType\":\"priorityRequest\",\"inBoundLane\":{\"lane\":13},\"outBoundLane\":{\"lane\":4}},\"duration\":10979}]},\"requestor\":{\"id\":{\"stationID\":2366845094},\"type\":{\"role\":\"publicTransport\"},\"position\":{\"position\":{\"latitude\":39.5904915,\"longitude\":-105.0913829,\"elevation\":1685.4},\"heading\":175.9000}}},\"dataType\":\"us.dot.its.jpo.ode.plugin.j2735.J2735SRM\"}}"; + + + @Test + public void testSrmGetAsOdeData() { + + SrmDecodedMessage srm = MockDecodedMessageGenerator.getSrmDecodedMessage(); + OdeData data = srmDecoder.getAsOdeData(srm.getAsn1Text()); + + OdeMsgMetadata metadata = data.getMetadata(); + + + // Copy over fields that might be different + metadata.setOdeReceivedAt("2024-05-15T20:02:18.273873Z"); + metadata.setSerialId(metadata.getSerialId().setStreamId("1809ff18-9251-42f0-a579-d629c1f6d60f")); + + assertEquals(data.toJson(), odeSrmDataReference); + + } + + @Test + public void testSrmGetAsOdeJson() throws XmlUtilsException{ + OdeSrmData srm = srmDecoder.getAsOdeJson(odeSrmDecodedXmlReference); + assertEquals(srm.toJson(), odeSrmDecodedDataReference); + } + +} diff --git a/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/decoderTests/SsmDecoderTests.java b/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/decoderTests/SsmDecoderTests.java new file mode 100644 index 00000000..6488fd5c --- /dev/null +++ b/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/decoderTests/SsmDecoderTests.java @@ -0,0 +1,58 @@ +package us.dot.its.jpo.ode.api.decoderTests; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import us.dot.its.jpo.ode.api.asn1.SsmDecoder; +import us.dot.its.jpo.ode.api.models.messages.SsmDecodedMessage; +import us.dot.its.jpo.ode.mockdata.MockDecodedMessageGenerator; +import us.dot.its.jpo.ode.model.OdeSsmData; +import us.dot.its.jpo.ode.model.OdeData; +import us.dot.its.jpo.ode.model.OdeMsgMetadata; + +import us.dot.its.jpo.ode.util.XmlUtils.XmlUtilsException; + + +@SpringBootTest +@RunWith(SpringRunner.class) +public class SsmDecoderTests { + + + @Autowired + SsmDecoder ssmDecoder; + + private String odeSsmDataReference = "{\"metadata\":{\"recordType\":\"ssmTx\",\"encodings\":[{\"elementName\":\"unsecuredData\",\"elementType\":\"MessageFrame\",\"encodingRule\":\"UPER\"}],\"payloadType\":\"us.dot.its.jpo.ode.model.OdeAsn1Payload\",\"serialId\":{\"streamId\":\"fc430f29-b761-4a2c-90fb-dc4c9f5d4e9c\",\"bundleSize\":1,\"bundleId\":0,\"recordId\":0,\"serialNumber\":0},\"odeReceivedAt\":\"2024-05-14T23:01:21.516531700Z\",\"schemaVersion\":6,\"maxDurationTime\":0,\"sanitized\":false,\"originIp\":\"user-upload\"},\"payload\":{\"dataType\":\"us.dot.its.jpo.ode.model.OdeHexByteArray\",\"data\":{\"bytes\":\"001e120000000005e9c04071a26614c06000040ba0\"}}}"; + private String odeSsmDecodedXmlReference = "ssmTxunsecuredDataMessageFrameUPERus.dot.its.jpo.ode.model.OdeAsn1Payloadf9eed695-0e43-4272-90a1-b879c663766d10002024-05-15T20:20:44.648878014Z60false172.18.0.1RSUMessageFrame30001211023668450943023"; + private String odeSsmDecodedDataReference = "{\"metadata\":{\"logFileName\":\"\",\"recordType\":\"ssmTx\",\"receivedMessageDetails\":{\"rxSource\":\"NA\"},\"payloadType\":\"us.dot.its.jpo.ode.model.OdeSsmPayload\",\"serialId\":{\"streamId\":\"f9eed695-0e43-4272-90a1-b879c663766d\",\"bundleSize\":1,\"bundleId\":0,\"recordId\":0,\"serialNumber\":0},\"odeReceivedAt\":\"2024-05-15T20:20:44.648878014Z\",\"schemaVersion\":6,\"maxDurationTime\":0,\"recordGeneratedAt\":\"\",\"sanitized\":false,\"odePacketID\":\"\",\"odeTimStartDateTime\":\"\",\"originIp\":\"172.18.0.1\",\"ssmSource\":\"RSU\"},\"payload\":{\"data\":{\"second\":0,\"status\":{\"signalStatus\":[{\"sequenceNumber\":0,\"id\":{\"id\":12110},\"sigStatus\":{\"signalStatusPackage\":[{\"requester\":{\"id\":{\"stationID\":2366845094},\"request\":3,\"sequenceNumber\":0,\"typeData\":{\"role\":\"publicTransport\"}},\"inboundOn\":{\"lane\":23},\"status\":\"granted\"}]}}]}},\"dataType\":\"us.dot.its.jpo.ode.plugin.j2735.J2735SSM\"}}"; + + + @Test + public void testSsmGetAsOdeData() { + + SsmDecodedMessage ssm = MockDecodedMessageGenerator.getSsmDecodedMessage(); + OdeData data = ssmDecoder.getAsOdeData(ssm.getAsn1Text()); + + OdeMsgMetadata metadata = data.getMetadata(); + + System.out.println(data); + // Copy over fields that might be different + metadata.setOdeReceivedAt("2024-05-14T23:01:21.516531700Z"); + metadata.setSerialId(metadata.getSerialId().setStreamId("fc430f29-b761-4a2c-90fb-dc4c9f5d4e9c")); + + assertEquals(data.toJson(), odeSsmDataReference); + + } + + @Test + public void testSsmGetAsOdeJson() throws XmlUtilsException{ + OdeSsmData ssm = ssmDecoder.getAsOdeJson(odeSsmDecodedXmlReference); + assertEquals(ssm.toJson(), odeSsmDecodedDataReference); + System.out.println(ssm); + } + +} diff --git a/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/decoderTests/TimDecoderTests.java b/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/decoderTests/TimDecoderTests.java new file mode 100644 index 00000000..1be98e6d --- /dev/null +++ b/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/decoderTests/TimDecoderTests.java @@ -0,0 +1,45 @@ +package us.dot.its.jpo.ode.api.decoderTests; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import us.dot.its.jpo.ode.api.asn1.TimDecoder; +import us.dot.its.jpo.ode.api.models.messages.TimDecodedMessage; +import us.dot.its.jpo.ode.mockdata.MockDecodedMessageGenerator; +import us.dot.its.jpo.ode.model.OdeData; +import us.dot.its.jpo.ode.model.OdeMsgMetadata; + + +@SpringBootTest +@RunWith(SpringRunner.class) +public class TimDecoderTests { + + + @Autowired + TimDecoder timDecoder; + + private String odeTimDataReference = "{\"metadata\":{\"recordType\":\"timMsg\",\"encodings\":[{\"elementName\":\"unsecuredData\",\"elementType\":\"MessageFrame\",\"encodingRule\":\"UPER\"}],\"payloadType\":\"us.dot.its.jpo.ode.model.OdeAsn1Payload\",\"serialId\":{\"streamId\":\"fc430f29-b761-4a2c-90fb-dc4c9f5d4e9c\",\"bundleSize\":1,\"bundleId\":0,\"recordId\":0,\"serialNumber\":0},\"odeReceivedAt\":\"2024-05-14T23:01:21.516531700Z\",\"schemaVersion\":6,\"maxDurationTime\":0,\"recordGeneratedBy\":\"RSU\",\"sanitized\":false,\"originIp\":\"user-upload\"},\"payload\":{\"dataType\":\"us.dot.its.jpo.ode.model.OdeHexByteArray\",\"data\":{\"bytes\":\"005f498718cca69ec1a04600000100105d9b46ec5be401003a0103810040038081d4001f80d07016da410000000000000bbc2b0f775d9b0309c271431fa166ee0a27fff93f136b8205a0a107fb2ef979f4c5bfaeec97e4ad70c2fb36cd9730becdb355cc2fd2a7556b160b98b46ab98ae62c185fa55efb468d5b4000000004e2863f42cddc144ff7980040401262cdd7b809c509f5c62cdd35519c507b9062cdcee129c505cf262cdca5ff9c50432c62cdc5d3d9c502e3e62cdc13e79c501e9262cdbca2d9c5013ee62cdb80359c500e6a62cdb36299c500bc862cdaec1d9c50093c62cdaa2109c5006ea1080203091a859eeebb36006001830001aad27f4ff7580001aad355e39b5880a30029d6585009ef808332d8d9f80c3855151b38c772f765007967ec1170bcb7937f5cb880a25a52863493bcb87570dbcb5abc6bfb2faec606cfa34eb95a24790b2017366d3aabe7729e\"}}}"; + + @Test + public void testTimGetAsOdeData() { + + TimDecodedMessage tim = MockDecodedMessageGenerator.getTimDecodedMessage(); + OdeData data = timDecoder.getAsOdeData(tim.getAsn1Text()); + + OdeMsgMetadata metadata = data.getMetadata(); + + System.out.println(data); + // Copy over fields that might be different + metadata.setOdeReceivedAt("2024-05-14T23:01:21.516531700Z"); + metadata.setSerialId(metadata.getSerialId().setStreamId("fc430f29-b761-4a2c-90fb-dc4c9f5d4e9c")); + + assertEquals(data.toJson(), odeTimDataReference); + + } + +} From f7ba0ecd9a9991c3aba06c8a42b7b7f2b0449d13 Mon Sep 17 00:00:00 2001 From: John-Wiens Date: Thu, 16 May 2024 08:58:19 -0600 Subject: [PATCH 19/29] Updated Pom to exclude redundant dependency --- api/jpo-conflictvisualizer-api/pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/api/jpo-conflictvisualizer-api/pom.xml b/api/jpo-conflictvisualizer-api/pom.xml index 50bec40a..790a2e66 100644 --- a/api/jpo-conflictvisualizer-api/pom.xml +++ b/api/jpo-conflictvisualizer-api/pom.xml @@ -77,6 +77,12 @@ org.springframework.boot spring-boot-starter-test test + + + com.vaadin.external.google + android-json + + org.springframework.kafka From d984522f5f3bf9f79b5aac359392ef341216b2c9 Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Fri, 24 May 2024 13:32:59 -0600 Subject: [PATCH 20/29] manipulating decoded messages --- .vscode/settings.json | 5 +- gui/src/apis/decoder-api.ts | 35 +++ gui/src/components/decoder/decoder-entry.tsx | 87 ++++++++ .../components/decoder/decoder-map-dialog.tsx | 47 ++++ gui/src/components/decoder/decoder-tables.tsx | 137 ++++++++++++ gui/src/components/map/map-component.tsx | 95 +++++++- gui/src/components/map/side-panel.tsx | 42 +++- gui/src/models/decoder.d.ts | 37 ++++ gui/src/models/imports/OdeBsmData.d.ts | 10 +- gui/src/pages/decoder.tsx | 202 ++++++++++++++++++ 10 files changed, 674 insertions(+), 23 deletions(-) create mode 100644 gui/src/apis/decoder-api.ts create mode 100644 gui/src/components/decoder/decoder-entry.tsx create mode 100644 gui/src/components/decoder/decoder-map-dialog.tsx create mode 100644 gui/src/components/decoder/decoder-tables.tsx create mode 100644 gui/src/models/decoder.d.ts create mode 100644 gui/src/pages/decoder.tsx diff --git a/.vscode/settings.json b/.vscode/settings.json index ba2fb168..57a6f30c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,4 @@ { - "java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx2G -Xms100m -Xlog:disable" -} \ No newline at end of file + "java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx2G -Xms100m -Xlog:disable", + "cSpell.words": ["uuidv"] +} diff --git a/gui/src/apis/decoder-api.ts b/gui/src/apis/decoder-api.ts new file mode 100644 index 00000000..446c39a3 --- /dev/null +++ b/gui/src/apis/decoder-api.ts @@ -0,0 +1,35 @@ +import { authApiHelper } from "./api-helper"; + +class DecoderApi { + async getIntersections({ token }): Promise { + var response = await authApiHelper.invokeApi({ + path: "/intersection/list", + token: token, + failureMessage: "Failed to retrieve intersection list", + }); + return response ?? []; + } + + async submitDecodeRequest({ + token, + data, + type, + }: { + token: string; + data: string; + type?: DECODER_MESSAGE_TYPE; + }): Promise { + var response = await authApiHelper.invokeApi({ + path: "/decoder/upload", + token: token, + method: "POST", + body: { + asn1Message: data, + type: type, + }, + }); + return response as DecoderApiResponseGeneric | undefined; + } +} + +export default new DecoderApi(); diff --git a/gui/src/components/decoder/decoder-entry.tsx b/gui/src/components/decoder/decoder-entry.tsx new file mode 100644 index 00000000..d252c266 --- /dev/null +++ b/gui/src/components/decoder/decoder-entry.tsx @@ -0,0 +1,87 @@ +import PerfectScrollbar from "react-perfect-scrollbar"; +import PropTypes from "prop-types"; +import { format } from "date-fns"; +import NextLink from "next/link"; +import { + Box, + Card, + Checkbox, + IconButton, + Table, + TableBody, + TableCell, + TableHead, + TablePagination, + TableRow, + TextField, + Typography, +} from "@mui/material"; +import React from "react"; +import MapRoundedIcon from "@mui/icons-material/MapRounded"; +import DeleteIcon from "@mui/icons-material/Delete"; +import CircularProgress from "@mui/material/CircularProgress"; + +// type DecoderApiResponseGeneric = { +// type: DECODER_MESSAGE_TYPE; +// odeReceivedAt: number; +// decoderMessages: string[]; +// j2735Paylaod: string; +// payload: any; +// }; + +type DecoderEntryProps = { + onSelected: (id: string) => void; + onTextChanged: (id: string, messageText: string) => void; + onDeleted: (id: string) => void; +}; + +export const DecoderEntry = (props: DecoderDataEntry & DecoderEntryProps) => { + const { id, status, selected, text, isGreyedOut, decodedResponse, onSelected, onTextChanged, onDeleted } = props; + + const handleCheckboxChange = () => { + onSelected(id); + }; + + const handleTextChange = (event: React.FocusEvent) => { + onTextChanged(id, event.target.value); + }; + + const handleDeleteClick = () => { + onDeleted(id); + }; + + const getCellColor = () => { + if (isGreyedOut) { + return "lightgray"; + } + + switch (status) { + case "NOT_STARTED": + return "white"; + case "IN_PROGRESS": + return "yellow"; + case "COMPLETED": + return "green"; + case "ERROR": + return "red"; + default: + return "white"; + } + }; + + return ( + + + + + + + + {status === "IN_PROGRESS" && } + + + + + + ); +}; diff --git a/gui/src/components/decoder/decoder-map-dialog.tsx b/gui/src/components/decoder/decoder-map-dialog.tsx new file mode 100644 index 00000000..457174e9 --- /dev/null +++ b/gui/src/components/decoder/decoder-map-dialog.tsx @@ -0,0 +1,47 @@ +import { Dialog, DialogTitle, Container, DialogActions, Button } from "@mui/material"; +import MapTab from "../map/map-component"; + +type Props = { + onClose: () => void; + open: boolean; + intersectionId: number; + roadRegulatorId: number; + map: ProcessedMap[]; + spat: ProcessedSpat[]; + bsm: OdeBsmData[]; +}; + +export const DecoderMapDialog = (props: Props) => { + const { onClose, open, intersectionId, roadRegulatorId, map, spat, bsm } = props; + + const handleClose = () => { + onClose(); + }; + + console.log("Map Dialog", map, spat, bsm); + + return ( + <> +

+ View Decoded Data + + + + + + + + + ); +}; diff --git a/gui/src/components/decoder/decoder-tables.tsx b/gui/src/components/decoder/decoder-tables.tsx new file mode 100644 index 00000000..1c14eed3 --- /dev/null +++ b/gui/src/components/decoder/decoder-tables.tsx @@ -0,0 +1,137 @@ +import PerfectScrollbar from "react-perfect-scrollbar"; +import { Box, Card, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from "@mui/material"; +import React from "react"; +import { DecoderEntry } from "./decoder-entry"; + +type DecoderTableProps = { + contents: DecoderDataEntry[]; + selectedIntersectionId: number; + onItemSelected: (id: string) => void; + onTextChanged: (id: string, messageText: string, type: DECODER_MESSAGE_TYPE) => void; + onItemDeleted: (id: string) => void; +}; + +export const DecoderTables = (props: DecoderTableProps) => { + const { contents, selectedIntersectionId, onItemSelected, onTextChanged, onItemDeleted } = props; + + const getIntersectionId = (decodedResponse: DecoderApiResponseGeneric | undefined) => { + if (!decodedResponse) { + return undefined; + } + + switch (decodedResponse.type) { + case "MAP": + const mapPayload = decodedResponse.processedMap; + return mapPayload?.properties?.intersectionId; + case "SPAT": + const spatPayload = decodedResponse.processedSpat; + return spatPayload?.intersectionId; + case "BSM": + return undefined; + } + }; + + const isGreyedOut = (intersectionId: number | undefined) => { + return ( + intersectionId !== undefined && selectedIntersectionId !== undefined && intersectionId !== selectedIntersectionId + ); + }; + + return ( + + + + + + + + MAP Messages + + + + {contents + .filter((v) => v.type === "MAP") + .map((entry) => { + return ( + onTextChanged(id, text, "MAP")} + onDeleted={onItemDeleted} + /> + ); + })} + +
+
+ + + + + + SPAT Messages + + + + {contents + .filter((v) => v.type === "SPAT") + .map((entry) => { + return ( + onTextChanged(id, text, "SPAT")} + onDeleted={onItemDeleted} + /> + ); + })} + +
+
+ + + + + + BSM Messages + + + + {contents + .filter((v) => v.type === "BSM") + .map((entry) => { + return ( + onTextChanged(id, text, "BSM")} + onDeleted={onItemDeleted} + /> + ); + })} + +
+
+
+
+
+ ); +}; diff --git a/gui/src/components/map/map-component.tsx b/gui/src/components/map/map-component.tsx index 24afe33f..00a41c74 100644 --- a/gui/src/components/map/map-component.tsx +++ b/gui/src/components/map/map-component.tsx @@ -24,7 +24,7 @@ import * as turf from "@turf/turf"; import { CompatClient, IMessage, Stomp } from "@stomp/stompjs"; import { set } from "date-fns"; -import { BarChart, XAxis, Bar, Cell, ResponsiveContainer, Tooltip } from 'recharts'; +import { BarChart, XAxis, Bar, Cell, ResponsiveContainer, Tooltip } from "recharts"; const { publicRuntimeConfig } = getConfig(); @@ -176,9 +176,37 @@ const markerLayer: LayerProps = { }, }; +const getTimestamp = (dt: any): number => { + try { + const dtFromString = Date.parse(dt as any as string); + if (isNaN(dtFromString)) { + if (dt > 1000000000000) { + return dt; // already in milliseconds + } else { + return dt * 1000; + } + } else { + return dtFromString; + } + } catch (e) { + console.error("Failed to parse timestamp from value: " + dt, e); + return 0; + } +}; + const generateQueryParams = ( - source: MessageMonitor.Notification | MessageMonitor.Event | Assessment | timestamp | undefined, - sourceDataType: "notification" | "event" | "assessment" | "timestamp" | undefined + source: + | MessageMonitor.Notification + | MessageMonitor.Event + | Assessment + | timestamp + | { + map: ProcessedMap[]; + spat: ProcessedSpat[]; + bsm: OdeBsmData[]; + } + | undefined, + sourceDataType: "notification" | "event" | "assessment" | "timestamp" | "exact" | undefined ) => { const startOffset = 1000 * 60 * 1; const endOffset = 1000 * 60 * 1; @@ -216,6 +244,39 @@ const generateQueryParams = ( eventDate: new Date(ts), vehicleId: undefined, }; + case "exact": + let startDate = undefined as number | undefined; + let endDate = undefined as number | undefined; + for (const map of (source as { map: ProcessedMap[] }).map) { + if (!startDate || Date.parse(map.properties.odeReceivedAt) < startDate) { + startDate = Date.parse(map.properties.odeReceivedAt); + } + if (!endDate || Date.parse(map.properties.odeReceivedAt) > endDate) { + endDate = Date.parse(map.properties.odeReceivedAt); + } + } + for (const spat of (source as { spat: ProcessedSpat[] }).spat) { + if (!startDate || spat.utcTimeStamp < startDate) { + startDate = spat.utcTimeStamp; + } + if (!endDate || spat.utcTimeStamp > endDate) { + endDate = spat.utcTimeStamp; + } + } + for (const bsm of (source as { bsm: OdeBsmData[] }).bsm) { + if (!startDate || Date.parse(bsm.metadata.odeReceivedAt) < startDate) { + startDate = Date.parse(bsm.metadata.odeReceivedAt); + } + if (!endDate || Date.parse(bsm.metadata.odeReceivedAt) > endDate) { + endDate = Date.parse(bsm.metadata.odeReceivedAt); + } + } + return { + startDate: new Date(startDate ?? Date.now()), + endDate: new Date(endDate ?? Date.now() + 1), + eventDate: new Date((startDate ?? Date.now(), endDate ?? Date.now() + 1) / 2), + vehicleId: undefined, + }; default: return { startDate: new Date(Date.now() - startOffset), @@ -231,8 +292,18 @@ type timestamp = { }; type MyProps = { - sourceData: MessageMonitor.Notification | MessageMonitor.Event | Assessment | timestamp | undefined; - sourceDataType: "notification" | "event" | "assessment" | "timestamp" | undefined; + sourceData: + | MessageMonitor.Notification + | MessageMonitor.Event + | Assessment + | timestamp + | { + map: ProcessedMap[]; + spat: ProcessedSpat[]; + bsm: OdeBsmData[]; + } + | undefined; + sourceDataType: "notification" | "event" | "assessment" | "timestamp" | "exact" | undefined; intersectionId: number | undefined; roadRegulatorId: number | undefined; loadOnNull?: boolean; @@ -768,7 +839,11 @@ const MapTab = (props: MyProps) => { let rawMap: ProcessedMap[] = []; let rawSpat: ProcessedSpat[] = []; let rawBsm: OdeBsmData[] = []; - if (importedMessageData == undefined) { + if (props.sourceDataType == "exact") { + rawMap = (props.sourceData as { map: ProcessedMap[] }).map; + rawSpat = (props.sourceData as { spat: ProcessedSpat[] }).spat; + rawBsm = (props.sourceData as { bsm: OdeBsmData[] }).bsm; + } else if (importedMessageData == undefined) { // ######################### Retrieve MAP Data ######################### const rawMapPromise = MessageMonitorApi.getMapMessages({ token: session?.accessToken, @@ -917,7 +992,7 @@ const MapTab = (props: MyProps) => { currentSpatData: ProcessedSpat[], currentBsmData: BsmFeatureCollection ) => { - if (currentMapData.length == 0) { + if (currentMapData.length == 0 && props.sourceDataType != "exact") { console.error("Did not attempt to render map, no map messages available:", currentMapData); return; } @@ -989,11 +1064,11 @@ const MapTab = (props: MyProps) => { }; } return () => {}; - }, [playbackModeActive]); + }, [playbackModeActive]); useEffect(() => { - setBsmByMinuteUpdated(true); -}, [bsmEventsByMinute]); + setBsmByMinuteUpdated(true); + }, [bsmEventsByMinute]); useEffect(() => { const endTime = getTimeRange(queryParams.startDate, queryParams.endDate); diff --git a/gui/src/components/map/side-panel.tsx b/gui/src/components/map/side-panel.tsx index 77209fe9..b8e07ef4 100644 --- a/gui/src/components/map/side-panel.tsx +++ b/gui/src/components/map/side-panel.tsx @@ -48,8 +48,18 @@ interface SidePanelProps { bsms: BsmFeatureCollection; events: MessageMonitor.Event[]; notifications: MessageMonitor.Notification[]; - sourceData: MessageMonitor.Notification | MessageMonitor.Event | Assessment | { timestamp: number } | undefined; - sourceDataType: "notification" | "event" | "assessment" | "timestamp" | undefined; + sourceData: + | MessageMonitor.Notification + | MessageMonitor.Event + | Assessment + | { timestamp: number } + | { + map: ProcessedMap[]; + spat: ProcessedSpat[]; + bsm: OdeBsmData[]; + } + | undefined; + sourceDataType: "notification" | "event" | "assessment" | "timestamp" | "exact" | undefined; } export const SidePanel = (props: SidePanelProps) => { @@ -67,15 +77,35 @@ export const SidePanel = (props: SidePanelProps) => { bsms: BsmFeatureCollection; events: MessageMonitor.Event[]; notifications: MessageMonitor.Notification[]; - sourceData: MessageMonitor.Notification | MessageMonitor.Event | Assessment | { timestamp: number } | undefined; - sourceDataType: "notification" | "event" | "assessment" | "timestamp" | undefined; + sourceData: + | MessageMonitor.Notification + | MessageMonitor.Event + | Assessment + | { timestamp: number } + | { + map: ProcessedMap[]; + spat: ProcessedSpat[]; + bsm: OdeBsmData[]; + } + | undefined; + sourceDataType: "notification" | "event" | "assessment" | "timestamp" | "exact" | undefined; } = props; const [open, setOpen] = useState(false); const getDataTable = ( - sourceData: MessageMonitor.Notification | MessageMonitor.Event | Assessment | { timestamp: number } | undefined, - sourceDataType: "notification" | "event" | "assessment" | "timestamp" | undefined + sourceData: + | MessageMonitor.Notification + | MessageMonitor.Event + | Assessment + | { timestamp: number } + | { + map: ProcessedMap[]; + spat: ProcessedSpat[]; + bsm: OdeBsmData[]; + } + | undefined, + sourceDataType: "notification" | "event" | "assessment" | "timestamp" | "exact" | undefined ) => { switch (sourceDataType) { case "notification": diff --git a/gui/src/models/decoder.d.ts b/gui/src/models/decoder.d.ts new file mode 100644 index 00000000..b7b5d2cc --- /dev/null +++ b/gui/src/models/decoder.d.ts @@ -0,0 +1,37 @@ +type DECODER_MESSAGE_TYPE = "SPAT" | "MAP" | "BSM"; + +type DecoderApiResponseGeneric = { + type: DECODER_MESSAGE_TYPE; + payload: any; + decodeTime: number; + decodeErrors: string; + asn1Text: string; + processedMap: ProcessedMap | undefined; + processedSpat: processedSpat | undefined; + bsm: OdeBsmData | undefined; +}; + +type DecoderApiResponseSpat = DecoderApiResponseGeneric & { + type: "SPAT"; + payload: ProcessedSpat; +}; +type DecoderApiResponseMap = DecoderApiResponseGeneric & { + type: "MAP"; + payload: ProcessedMap; +}; +type DecoderApiResponseBsm = DecoderApiResponseGeneric & { + type: "BSM"; + payload: OdeBsmData; +}; + +type DECODER_PROGRESS_TYPE = "NOT_STARTED" | "IN_PROGRESS" | "COMPLETED" | "ERROR"; + +type DecoderDataEntry = { + id: string; + type: DECODER_MESSAGE_TYPE; + status: DECODER_PROGRESS_TYPE; + selected: boolean; + isGreyedOut: boolean; + text: string; + decodedResponse: DecoderApiResponseGeneric | undefined; +}; diff --git a/gui/src/models/imports/OdeBsmData.d.ts b/gui/src/models/imports/OdeBsmData.d.ts index 8c5591ab..32cde017 100644 --- a/gui/src/models/imports/OdeBsmData.d.ts +++ b/gui/src/models/imports/OdeBsmData.d.ts @@ -4,14 +4,14 @@ type OdeBsmData = { }; type OdeMsgMetadata = { - payloadType: String; + payloadType: string; serialId: SerialId; - odeReceivedAt: String; + odeReceivedAt: string; schemaVersion: number; maxDurationTime: number; - odePacketID: String; - odeTimStartDateTime: String; - recordGeneratedAt: String; + odePacketID: string; + odeTimStartDateTime: string; + recordGeneratedAt: string; recordGeneratedBy: GeneratedBy; sanitized: boolean; }; diff --git a/gui/src/pages/decoder.tsx b/gui/src/pages/decoder.tsx new file mode 100644 index 00000000..34dc03f6 --- /dev/null +++ b/gui/src/pages/decoder.tsx @@ -0,0 +1,202 @@ +import { useEffect, useState } from "react"; +import Head from "next/head"; +import { Box, Button, Container } from "@mui/material"; +import DecoderApi from "../apis/decoder-api"; +import { DashboardLayout } from "../components/dashboard-layout"; +import { useDashboardContext } from "../contexts/dashboard-context"; +import { useSession } from "next-auth/react"; +import MapDialog from "../components/intersection-selector/intersection-selector-dialog"; +import { DecoderTables } from "../components/decoder/decoder-tables"; +import { v4 as uuidv4 } from "uuid"; +import { DecoderMapDialog } from "../components/decoder/decoder-map-dialog"; +import { Plus as PlusIcon } from "../icons/plus"; + +const DecoderPage = () => { + const { intersectionId } = useDashboardContext(); + const { data: session } = useSession(); + + const [openMapDialog, setOpenMapDialog] = useState(false); + const [data, setData] = useState({} as { [id: string]: DecoderDataEntry }); + + console.log("Data", data); + + useEffect(() => { + const freshData = [] as DecoderDataEntry[]; + for (let i = 0; i < 3; i++) { + freshData.push({ + id: uuidv4(), + type: i % 3 == 0 ? "MAP" : i % 3 == 1 ? "SPAT" : "BSM", + status: "NOT_STARTED", + text: "", + selected: false, + isGreyedOut: false, + decodedResponse: undefined, + }); + } + setData(freshData.reduce((acc, entry) => ({ ...acc, [entry.id]: entry }), {})); + }, []); + + const submitDecoderRequest = (data: string, type: DECODER_MESSAGE_TYPE) => { + if (session?.accessToken) { + return DecoderApi.submitDecodeRequest({ + token: session.accessToken, + data, + type, + }); + } else { + console.error("Did not attempt to update user automatically. Access token:", Boolean(session?.accessToken)); + return undefined; + } + }; + + const onTextChanged = (id: string, text: string, type: DECODER_MESSAGE_TYPE) => { + setData((prevData) => { + submitDecoderRequest(text, type)?.then((response) => { + setData((prevData) => { + return { + ...prevData, + [id]: { + ...prevData[id], + decodedResponse: response, + status: text == "" ? "NOT_STARTED" : response == undefined ? "ERROR" : "COMPLETED", + }, + }; + }); + }); + let newEntry = {}; + if (prevData[id].text != undefined) { + let newId = uuidv4(); + newEntry[newId] = { + id: newId, + type: type, + status: "NOT_STARTED", + text: "", + selected: false, + isGreyedOut: false, + decodedResponse: undefined, + }; + } + return { + ...prevData, + ...newEntry, + [id]: { + id: id, + type: type, + status: "IN_PROGRESS", + selected: false, + isGreyedOut: false, + text: text, + decodedResponse: undefined, + }, + }; + }); + }; + + const onItemDeleted = (id: string) => { + setData((prevData) => { + delete prevData[id]; + return { ...prevData }; + }); + }; + + const onItemSelected = (id: string) => { + setData((prevData) => { + return { + ...prevData, + [id]: { + ...prevData[id], + selected: !prevData[id].selected, + }, + }; + }); + }; + + return ( + <> + + ASN.1 Decoder + + + {/* + +
+ + Query + +
+
+ + + +
*/} + + + + + + +
+ { + setOpenMapDialog(false); + }} + intersectionId={-1} + roadRegulatorId={-1} + map={Object.values(data) + .filter((v) => v.type === "MAP" && v.status == "COMPLETED") + .map((v) => v.decodedResponse?.processedMap!)} + spat={Object.values(data) + .filter((v) => v.type === "SPAT" && v.status == "COMPLETED") + .map((v) => v.decodedResponse?.processedSpat!)} + bsm={Object.values(data) + .filter((v) => v.type === "BSM" && v.status == "COMPLETED") + .map((v) => v.decodedResponse?.bsm!)} + /> + + ); +}; + +DecoderPage.getLayout = (page) => {page}; + +export default DecoderPage; From a6420e26ddf2452507f3b5783d368e1eae8994c2 Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Fri, 24 May 2024 13:57:44 -0600 Subject: [PATCH 21/29] rendering decoder data on map --- gui/src/components/map/map-component.tsx | 37 +++++++++++++++++------- gui/src/models/imports/OdeBsmData.d.ts | 2 +- gui/src/models/imports/ProcessedMap.d.ts | 2 +- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/gui/src/components/map/map-component.tsx b/gui/src/components/map/map-component.tsx index 1eae014c..bbabf599 100644 --- a/gui/src/components/map/map-component.tsx +++ b/gui/src/components/map/map-component.tsx @@ -248,11 +248,11 @@ const generateQueryParams = ( let startDate = undefined as number | undefined; let endDate = undefined as number | undefined; for (const map of (source as { map: ProcessedMap[] }).map) { - if (!startDate || Date.parse(map.properties.odeReceivedAt) < startDate) { - startDate = Date.parse(map.properties.odeReceivedAt); + if (!startDate || getTimestamp(map.properties.odeReceivedAt) < startDate) { + startDate = getTimestamp(map.properties.odeReceivedAt); } - if (!endDate || Date.parse(map.properties.odeReceivedAt) > endDate) { - endDate = Date.parse(map.properties.odeReceivedAt); + if (!endDate || getTimestamp(map.properties.odeReceivedAt) > endDate) { + endDate = getTimestamp(map.properties.odeReceivedAt); } } for (const spat of (source as { spat: ProcessedSpat[] }).spat) { @@ -264,11 +264,11 @@ const generateQueryParams = ( } } for (const bsm of (source as { bsm: OdeBsmData[] }).bsm) { - if (!startDate || Date.parse(bsm.metadata.odeReceivedAt) < startDate) { - startDate = Date.parse(bsm.metadata.odeReceivedAt); + if (!startDate || getTimestamp(bsm.metadata.odeReceivedAt) < startDate) { + startDate = getTimestamp(bsm.metadata.odeReceivedAt); } - if (!endDate || Date.parse(bsm.metadata.odeReceivedAt) > endDate) { - endDate = Date.parse(bsm.metadata.odeReceivedAt); + if (!endDate || getTimestamp(bsm.metadata.odeReceivedAt) > endDate) { + endDate = getTimestamp(bsm.metadata.odeReceivedAt); } } return { @@ -842,9 +842,24 @@ const MapTab = (props: MyProps) => { let rawSpat: ProcessedSpat[] = []; let rawBsm: OdeBsmData[] = []; if (props.sourceDataType == "exact") { - rawMap = (props.sourceData as { map: ProcessedMap[] }).map; - rawSpat = (props.sourceData as { spat: ProcessedSpat[] }).spat; - rawBsm = (props.sourceData as { bsm: OdeBsmData[] }).bsm; + rawMap = (props.sourceData as { map: ProcessedMap[] }).map.map((map) => ({ + ...map, + properties: { + ...map.properties, + odeReceivedAt: getTimestamp(map.properties.odeReceivedAt), + }, + })); + rawSpat = (props.sourceData as { spat: ProcessedSpat[] }).spat.map((spat) => ({ + ...spat, + utcTimeStamp: getTimestamp(spat.utcTimeStamp), + })); + rawBsm = (props.sourceData as { bsm: OdeBsmData[] }).bsm.map((bsm) => ({ + ...bsm, + metadata: { + ...bsm.metadata, + odeReceivedAt: getTimestamp(bsm.metadata.odeReceivedAt), + }, + })); } else if (importedMessageData == undefined) { // ######################### Retrieve MAP Data ######################### const rawMapPromise = MessageMonitorApi.getMapMessages({ diff --git a/gui/src/models/imports/OdeBsmData.d.ts b/gui/src/models/imports/OdeBsmData.d.ts index 32cde017..a1beb080 100644 --- a/gui/src/models/imports/OdeBsmData.d.ts +++ b/gui/src/models/imports/OdeBsmData.d.ts @@ -6,7 +6,7 @@ type OdeBsmData = { type OdeMsgMetadata = { payloadType: string; serialId: SerialId; - odeReceivedAt: string; + odeReceivedAt: metadata; schemaVersion: number; maxDurationTime: number; odePacketID: string; diff --git a/gui/src/models/imports/ProcessedMap.d.ts b/gui/src/models/imports/ProcessedMap.d.ts index 0bb1cf49..79f17c00 100644 --- a/gui/src/models/imports/ProcessedMap.d.ts +++ b/gui/src/models/imports/ProcessedMap.d.ts @@ -6,7 +6,7 @@ type ProcessedMap = { type MapSharedProperties = { messageType: string; - odeReceivedAt: string; + odeReceivedAt: number; originIp: string; intersectionName?: string; region?: number; From be6c30269eb8b66af9a94097ce8cfff30ab07a89 Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Sun, 26 May 2024 10:23:46 -0600 Subject: [PATCH 22/29] Debugging SPAT rendering error --- gui/src/components/dashboard-sidebar.tsx | 6 + gui/src/components/decoder/decoder-entry.tsx | 73 ++++++++-- gui/src/components/decoder/decoder-tables.tsx | 129 +++++++++++++++--- gui/src/models/imports/OdeBsmData.d.ts | 1 + gui/src/pages/decoder.tsx | 76 ++++++++--- 5 files changed, 241 insertions(+), 44 deletions(-) diff --git a/gui/src/components/dashboard-sidebar.tsx b/gui/src/components/dashboard-sidebar.tsx index 67413903..67fd968d 100644 --- a/gui/src/components/dashboard-sidebar.tsx +++ b/gui/src/components/dashboard-sidebar.tsx @@ -16,6 +16,7 @@ import MapIcon from "@mui/icons-material/Map"; import TimelineIcon from "@mui/icons-material/Timeline"; import NotificationsIcon from "@mui/icons-material/Notifications"; import ArticleIcon from "@mui/icons-material/Article"; +import SubtitlesIcon from "@mui/icons-material/Subtitles"; import { Logo } from "./logo"; import { NavItem } from "./nav-item"; import React from "react"; @@ -49,6 +50,11 @@ const generalItems = [ icon: , title: "Data Selector", }, + { + path: "/decoder", + icon: , + title: "ASN.1 Decoder", + }, { path: "/configuration", icon: , diff --git a/gui/src/components/decoder/decoder-entry.tsx b/gui/src/components/decoder/decoder-entry.tsx index d252c266..74edd116 100644 --- a/gui/src/components/decoder/decoder-entry.tsx +++ b/gui/src/components/decoder/decoder-entry.tsx @@ -19,6 +19,7 @@ import { import React from "react"; import MapRoundedIcon from "@mui/icons-material/MapRounded"; import DeleteIcon from "@mui/icons-material/Delete"; +import DownloadIcon from "@mui/icons-material/Download"; import CircularProgress from "@mui/material/CircularProgress"; // type DecoderApiResponseGeneric = { @@ -36,7 +37,26 @@ type DecoderEntryProps = { }; export const DecoderEntry = (props: DecoderDataEntry & DecoderEntryProps) => { - const { id, status, selected, text, isGreyedOut, decodedResponse, onSelected, onTextChanged, onDeleted } = props; + const { id, status, selected, text, type, isGreyedOut, decodedResponse, onSelected, onTextChanged, onDeleted } = + props; + + const getIntersectionId = (decodedResponse: DecoderApiResponseGeneric | undefined) => { + if (!decodedResponse) { + return undefined; + } + + switch (decodedResponse.type) { + case "MAP": + const mapPayload = decodedResponse.processedMap; + return mapPayload?.properties?.intersectionId; + case "SPAT": + const spatPayload = decodedResponse.processedSpat; + return spatPayload?.intersectionId; + case "BSM": + const bsmPayload = decodedResponse.bsm; + return bsmPayload?.metadata.originIp; + } + }; const handleCheckboxChange = () => { onSelected(id); @@ -50,18 +70,48 @@ export const DecoderEntry = (props: DecoderDataEntry & DecoderEntryProps) => { onDeleted(id); }; - const getCellColor = () => { - if (isGreyedOut) { - return "lightgray"; + const handleDownloadClick = () => { + let contents = "{}"; + switch (type) { + case "MAP": + contents = JSON.stringify(decodedResponse?.processedMap ?? {}); + break; + case "SPAT": + contents = JSON.stringify(decodedResponse?.processedSpat ?? {}); + break; + case "BSM": + contents = JSON.stringify(decodedResponse?.bsm ?? {}); + break; } + if (contents !== "{}") { + const key = getIntersectionId(decodedResponse); + downloadJsonFile(contents, `${type}_${key}_decoded_${new Date().toISOString()}.json`); + } + }; + + const downloadJsonFile = (contents: any, name: string) => { + const element = document.createElement("a"); + const file = new Blob([JSON.stringify(contents)], { + type: "text/plain", + }); + element.href = URL.createObjectURL(file); + element.download = name; + document.body.appendChild(element); // Required for this to work in FireFox + element.click(); + }; + const getCellColor = () => { switch (status) { case "NOT_STARTED": return "white"; case "IN_PROGRESS": return "yellow"; case "COMPLETED": - return "green"; + if (isGreyedOut) { + return "#566454"; + } else { + return "#448b3b"; + } case "ERROR": return "red"; default: @@ -72,14 +122,21 @@ export const DecoderEntry = (props: DecoderDataEntry & DecoderEntryProps) => { return ( - - + {type == "MAP" && } + + + + {status === "IN_PROGRESS" && } - + diff --git a/gui/src/components/decoder/decoder-tables.tsx b/gui/src/components/decoder/decoder-tables.tsx index 1c14eed3..452f9907 100644 --- a/gui/src/components/decoder/decoder-tables.tsx +++ b/gui/src/components/decoder/decoder-tables.tsx @@ -1,18 +1,30 @@ import PerfectScrollbar from "react-perfect-scrollbar"; -import { Box, Card, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from "@mui/material"; +import { Box, Card, IconButton, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from "@mui/material"; import React from "react"; import { DecoderEntry } from "./decoder-entry"; +import DownloadIcon from "@mui/icons-material/Download"; type DecoderTableProps = { contents: DecoderDataEntry[]; - selectedIntersectionId: number; + selectedIntersectionId: number | undefined; + selectedMapMessageId: string | undefined; + selectedRsuIp: string | undefined; onItemSelected: (id: string) => void; onTextChanged: (id: string, messageText: string, type: DECODER_MESSAGE_TYPE) => void; onItemDeleted: (id: string) => void; + onFileUploaded: (contents: string[], type: DECODER_MESSAGE_TYPE) => void; }; export const DecoderTables = (props: DecoderTableProps) => { - const { contents, selectedIntersectionId, onItemSelected, onTextChanged, onItemDeleted } = props; + const { + contents, + selectedIntersectionId, + selectedMapMessageId, + selectedRsuIp, + onItemSelected, + onTextChanged, + onItemDeleted, + } = props; const getIntersectionId = (decodedResponse: DecoderApiResponseGeneric | undefined) => { if (!decodedResponse) { @@ -27,25 +39,80 @@ export const DecoderTables = (props: DecoderTableProps) => { const spatPayload = decodedResponse.processedSpat; return spatPayload?.intersectionId; case "BSM": - return undefined; + const bsmPayload = decodedResponse.bsm; + return bsmPayload?.metadata.originIp; } }; const isGreyedOut = (intersectionId: number | undefined) => { - return ( - intersectionId !== undefined && selectedIntersectionId !== undefined && intersectionId !== selectedIntersectionId - ); + return selectedIntersectionId === undefined || intersectionId !== selectedIntersectionId; + }; + + const isGreyedOutIp = (rsuIp: string | undefined) => { + return (selectedRsuIp === undefined || rsuIp !== selectedRsuIp) && rsuIp != ""; + }; + + const dataFileUploaded = (event, type: DECODER_MESSAGE_TYPE) => { + const file = event.target.files[0]; + if (file) { + const reader = new FileReader(); + reader.onload = function (evt) { + try { + // Split the file content by new lines and remove empty strings + const contents = evt.target?.result as string; + const lines = contents.split("\n").filter((line) => line.trim() !== ""); + props.onFileUploaded(lines, type); + // Now lines is an array of strings from the file + } catch (e) { + console.error("Error reading uploaded decoder file", e); + } + }; + reader.readAsText(file); + } + }; + + const handleDownloadClick = (type: DECODER_MESSAGE_TYPE) => { + let files = contents.filter((v) => v.type === type && v.decodedResponse != undefined).map((v) => v.decodedResponse); + if (files.length > 0) { + downloadJsonFile(files, `${type}_decoded_${new Date().toISOString()}.json`); + } + }; + + const downloadJsonFile = (contents: any, name: string) => { + const element = document.createElement("a"); + const file = new Blob([JSON.stringify(contents)], { + type: "text/plain", + }); + element.href = URL.createObjectURL(file); + element.download = name; + document.body.appendChild(element); // Required for this to work in FireFox + element.click(); }; return ( - + - MAP Messages + + MAP Messages + dataFileUploaded(event, "MAP")} + title="Your custom description here" + /> + handleDownloadClick("MAP")} + style={{ position: "absolute", right: 0, top: 5 }} + > + + + @@ -58,9 +125,9 @@ export const DecoderTables = (props: DecoderTableProps) => { status={entry.status} type={entry.type} text={entry.text} - isGreyedOut={isGreyedOut(getIntersectionId(entry.decodedResponse))} + isGreyedOut={entry.id !== selectedMapMessageId} decodedResponse={entry.decodedResponse} - selected={entry.selected} + selected={entry.id == selectedMapMessageId} onSelected={onItemSelected} onTextChanged={(id, text) => onTextChanged(id, text, "MAP")} onDeleted={onItemDeleted} @@ -75,7 +142,22 @@ export const DecoderTables = (props: DecoderTableProps) => {
- SPAT Messages + + SPAT Messages + dataFileUploaded(event, "SPAT")} + title="Your custom description here" + /> + handleDownloadClick("SPAT")} + style={{ position: "absolute", right: 0, top: 5 }} + > + + + @@ -90,7 +172,7 @@ export const DecoderTables = (props: DecoderTableProps) => { text={entry.text} isGreyedOut={isGreyedOut(getIntersectionId(entry.decodedResponse))} decodedResponse={entry.decodedResponse} - selected={entry.selected} + selected={false} onSelected={onItemSelected} onTextChanged={(id, text) => onTextChanged(id, text, "SPAT")} onDeleted={onItemDeleted} @@ -105,7 +187,22 @@ export const DecoderTables = (props: DecoderTableProps) => {
- BSM Messages + + BSM Messages + dataFileUploaded(event, "BSM")} + title="Your custom description here" + /> + handleDownloadClick("BSM")} + style={{ position: "absolute", right: 0, top: 5 }} + > + + + @@ -118,9 +215,9 @@ export const DecoderTables = (props: DecoderTableProps) => { status={entry.status} type={entry.type} text={entry.text} - isGreyedOut={isGreyedOut(getIntersectionId(entry.decodedResponse))} + isGreyedOut={isGreyedOutIp(getIntersectionId(entry.decodedResponse))} decodedResponse={entry.decodedResponse} - selected={entry.selected} + selected={false} onSelected={onItemSelected} onTextChanged={(id, text) => onTextChanged(id, text, "BSM")} onDeleted={onItemDeleted} diff --git a/gui/src/models/imports/OdeBsmData.d.ts b/gui/src/models/imports/OdeBsmData.d.ts index a1beb080..94754d75 100644 --- a/gui/src/models/imports/OdeBsmData.d.ts +++ b/gui/src/models/imports/OdeBsmData.d.ts @@ -14,6 +14,7 @@ type OdeMsgMetadata = { recordGeneratedAt: string; recordGeneratedBy: GeneratedBy; sanitized: boolean; + originIp: string; }; type GeneratedBy = "TMC" | "OBU" | "RSU" | "TMC_VIA_SAT" | "TMC_VIA_SNMP" | "UNKNOWN"; diff --git a/gui/src/pages/decoder.tsx b/gui/src/pages/decoder.tsx index 34dc03f6..2eb44513 100644 --- a/gui/src/pages/decoder.tsx +++ b/gui/src/pages/decoder.tsx @@ -10,13 +10,16 @@ import { DecoderTables } from "../components/decoder/decoder-tables"; import { v4 as uuidv4 } from "uuid"; import { DecoderMapDialog } from "../components/decoder/decoder-map-dialog"; import { Plus as PlusIcon } from "../icons/plus"; +import MapIcon from "@mui/icons-material/Map"; const DecoderPage = () => { - const { intersectionId } = useDashboardContext(); const { data: session } = useSession(); const [openMapDialog, setOpenMapDialog] = useState(false); const [data, setData] = useState({} as { [id: string]: DecoderDataEntry }); + const [selectedMapMessage, setSelectedMapMessage] = useState( + undefined as undefined | { id: string; intersectionId: number; rsuIp: string } + ); console.log("Data", data); @@ -93,20 +96,56 @@ const DecoderPage = () => { }; const onItemDeleted = (id: string) => { - setData((prevData) => { - delete prevData[id]; - return { ...prevData }; - }); + if (data[id]?.text != "") { + setData((prevData) => { + delete prevData[id]; + return { ...prevData }; + }); + } }; const onItemSelected = (id: string) => { + const intersectionId = data[id]?.decodedResponse?.processedMap?.properties?.intersectionId; + const rsuIp = data[id]?.decodedResponse?.processedMap?.properties?.originIp; + if (intersectionId) { + setSelectedMapMessage({ id, intersectionId, rsuIp: rsuIp! }); + } + }; + + const onFileUploaded = (contents: string[], type: DECODER_MESSAGE_TYPE) => { setData((prevData) => { + const textToIds: { [text: string]: string } = {}; + contents.forEach((text) => + submitDecoderRequest(text, type)?.then((response) => { + const id = uuidv4(); + textToIds[text] = id; + setData((prevData) => { + return { + ...prevData, + [id]: { + ...prevData[text], + decodedResponse: response, + status: text == "" ? "NOT_STARTED" : response == undefined ? "ERROR" : "COMPLETED", + }, + }; + }); + }) + ); + let newEntries = {}; + contents.forEach((text) => { + newEntries[textToIds[text]] = { + id: textToIds[text], + type: type, + status: "IN_PROGRESS", + text: text, + selected: false, + isGreyedOut: false, + decodedResponse: undefined, + }; + }); return { ...prevData, - [id]: { - ...prevData[id], - selected: !prevData[id].selected, - }, + ...newEntries, }; }); }; @@ -147,32 +186,29 @@ const DecoderPage = () => { /> */} - + - + From d2bbbec218b83731b027e6f87a8a6579fa855f98 Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Wed, 29 May 2024 02:14:37 -0600 Subject: [PATCH 23/29] Embedding decoder map in page --- gui/src/components/decoder/decoder-entry.tsx | 22 ++- gui/src/components/decoder/decoder-tables.tsx | 7 +- gui/src/components/map/map-component.tsx | 19 +-- gui/src/models/decoder.d.ts | 1 + gui/src/pages/decoder.tsx | 144 +++++++++++------- 5 files changed, 124 insertions(+), 69 deletions(-) diff --git a/gui/src/components/decoder/decoder-entry.tsx b/gui/src/components/decoder/decoder-entry.tsx index 74edd116..fcc9da59 100644 --- a/gui/src/components/decoder/decoder-entry.tsx +++ b/gui/src/components/decoder/decoder-entry.tsx @@ -21,6 +21,7 @@ import MapRoundedIcon from "@mui/icons-material/MapRounded"; import DeleteIcon from "@mui/icons-material/Delete"; import DownloadIcon from "@mui/icons-material/Download"; import CircularProgress from "@mui/material/CircularProgress"; +import { getTimestamp } from "../map/map-component"; // type DecoderApiResponseGeneric = { // type: DECODER_MESSAGE_TYPE; @@ -37,8 +38,19 @@ type DecoderEntryProps = { }; export const DecoderEntry = (props: DecoderDataEntry & DecoderEntryProps) => { - const { id, status, selected, text, type, isGreyedOut, decodedResponse, onSelected, onTextChanged, onDeleted } = - props; + const { + id, + status, + selected, + text, + type, + isGreyedOut, + decodedResponse, + timestamp, + onSelected, + onTextChanged, + onDeleted, + } = props; const getIntersectionId = (decodedResponse: DecoderApiResponseGeneric | undefined) => { if (!decodedResponse) { @@ -108,7 +120,7 @@ export const DecoderEntry = (props: DecoderDataEntry & DecoderEntryProps) => { return "yellow"; case "COMPLETED": if (isGreyedOut) { - return "#566454"; + return "#51634e"; } else { return "#448b3b"; } @@ -122,7 +134,9 @@ export const DecoderEntry = (props: DecoderDataEntry & DecoderEntryProps) => { return ( - {type == "MAP" && } + {type == "MAP" && text != "" && } + {timestamp && {format(new Date(timestamp), "yyyy-MM-dd HH:mm:ss")}} +

diff --git a/gui/src/components/decoder/decoder-tables.tsx b/gui/src/components/decoder/decoder-tables.tsx index 452f9907..c2a13682 100644 --- a/gui/src/components/decoder/decoder-tables.tsx +++ b/gui/src/components/decoder/decoder-tables.tsx @@ -78,9 +78,9 @@ export const DecoderTables = (props: DecoderTableProps) => { } }; - const downloadJsonFile = (contents: any, name: string) => { + const downloadJsonFile = (contents: any, name: string, alreadyStringified = false) => { const element = document.createElement("a"); - const file = new Blob([JSON.stringify(contents)], { + const file = new Blob([alreadyStringified ? contents : JSON.stringify(contents)], { type: "text/plain", }); element.href = URL.createObjectURL(file); @@ -128,6 +128,7 @@ export const DecoderTables = (props: DecoderTableProps) => { isGreyedOut={entry.id !== selectedMapMessageId} decodedResponse={entry.decodedResponse} selected={entry.id == selectedMapMessageId} + timestamp={entry.timestamp} onSelected={onItemSelected} onTextChanged={(id, text) => onTextChanged(id, text, "MAP")} onDeleted={onItemDeleted} @@ -173,6 +174,7 @@ export const DecoderTables = (props: DecoderTableProps) => { isGreyedOut={isGreyedOut(getIntersectionId(entry.decodedResponse))} decodedResponse={entry.decodedResponse} selected={false} + timestamp={entry.timestamp} onSelected={onItemSelected} onTextChanged={(id, text) => onTextChanged(id, text, "SPAT")} onDeleted={onItemDeleted} @@ -218,6 +220,7 @@ export const DecoderTables = (props: DecoderTableProps) => { isGreyedOut={isGreyedOutIp(getIntersectionId(entry.decodedResponse))} decodedResponse={entry.decodedResponse} selected={false} + timestamp={entry.timestamp} onSelected={onItemSelected} onTextChanged={(id, text) => onTextChanged(id, text, "BSM")} onDeleted={onItemDeleted} diff --git a/gui/src/components/map/map-component.tsx b/gui/src/components/map/map-component.tsx index bbabf599..3934086a 100644 --- a/gui/src/components/map/map-component.tsx +++ b/gui/src/components/map/map-component.tsx @@ -176,7 +176,7 @@ const markerLayer: LayerProps = { }, }; -const getTimestamp = (dt: any): number => { +export const getTimestamp = (dt: any): number => { try { const dtFromString = Date.parse(dt as any as string); if (isNaN(dtFromString)) { @@ -247,6 +247,7 @@ const generateQueryParams = ( case "exact": let startDate = undefined as number | undefined; let endDate = undefined as number | undefined; + for (const map of (source as { map: ProcessedMap[] }).map) { if (!startDate || getTimestamp(map.properties.odeReceivedAt) < startDate) { startDate = getTimestamp(map.properties.odeReceivedAt); @@ -256,11 +257,11 @@ const generateQueryParams = ( } } for (const spat of (source as { spat: ProcessedSpat[] }).spat) { - if (!startDate || spat.utcTimeStamp < startDate) { - startDate = spat.utcTimeStamp; + if (!startDate || getTimestamp(spat.utcTimeStamp) < startDate) { + startDate = getTimestamp(spat.utcTimeStamp); } - if (!endDate || spat.utcTimeStamp > endDate) { - endDate = spat.utcTimeStamp; + if (!endDate || getTimestamp(spat.utcTimeStamp) > endDate) { + endDate = getTimestamp(spat.utcTimeStamp); } } for (const bsm of (source as { bsm: OdeBsmData[] }).bsm) { @@ -274,7 +275,7 @@ const generateQueryParams = ( return { startDate: new Date(startDate ?? Date.now()), endDate: new Date(endDate ?? Date.now() + 1), - eventDate: new Date((startDate ?? Date.now(), endDate ?? Date.now() + 1) / 2), + eventDate: new Date((startDate ?? Date.now()) / 2 + (endDate ?? Date.now() + 1) / 2), vehicleId: undefined, }; default: @@ -961,7 +962,7 @@ const MapTab = (props: MyProps) => { setMapData(latestMapMessage); setMapSpatTimes((prevValue) => ({ ...prevValue, - mapTime: latestMapMessage.properties.odeReceivedAt as unknown as number, + mapTime: getTimestamp(latestMapMessage.properties.odeReceivedAt) / 1000, })); setMapSignalGroups(mapSignalGroupsLocal); if (latestMapMessage != null) { @@ -977,7 +978,7 @@ const MapTab = (props: MyProps) => { setSpatSignalGroups(spatSignalGroupsLocal); // ######################### BSMs ######################### - if (!importedMessageData) { + if (!importedMessageData && props.sourceDataType != "exact") { const rawBsmPromise = MessageMonitorApi.getBsmMessages({ token: session?.accessToken, vehicleId: queryParams.vehicleId, @@ -1020,7 +1021,7 @@ const MapTab = (props: MyProps) => { setMapData(latestMapMessage); setMapSpatTimes((prevValue) => ({ ...prevValue, - mapTime: latestMapMessage.properties.odeReceivedAt as unknown as number, + mapTime: getTimestamp(latestMapMessage.properties.odeReceivedAt) / 1000, })); setMapSignalGroups(mapSignalGroupsLocal); if (latestMapMessage != null) { diff --git a/gui/src/models/decoder.d.ts b/gui/src/models/decoder.d.ts index b7b5d2cc..53533fb9 100644 --- a/gui/src/models/decoder.d.ts +++ b/gui/src/models/decoder.d.ts @@ -32,6 +32,7 @@ type DecoderDataEntry = { status: DECODER_PROGRESS_TYPE; selected: boolean; isGreyedOut: boolean; + timestamp?: number | undefined; text: string; decodedResponse: DecoderApiResponseGeneric | undefined; }; diff --git a/gui/src/pages/decoder.tsx b/gui/src/pages/decoder.tsx index 2eb44513..4aa3c4bc 100644 --- a/gui/src/pages/decoder.tsx +++ b/gui/src/pages/decoder.tsx @@ -1,16 +1,13 @@ import { useEffect, useState } from "react"; import Head from "next/head"; -import { Box, Button, Container } from "@mui/material"; +import { Box, Button, Container, Grid, Typography } from "@mui/material"; import DecoderApi from "../apis/decoder-api"; import { DashboardLayout } from "../components/dashboard-layout"; -import { useDashboardContext } from "../contexts/dashboard-context"; import { useSession } from "next-auth/react"; -import MapDialog from "../components/intersection-selector/intersection-selector-dialog"; import { DecoderTables } from "../components/decoder/decoder-tables"; import { v4 as uuidv4 } from "uuid"; -import { DecoderMapDialog } from "../components/decoder/decoder-map-dialog"; -import { Plus as PlusIcon } from "../icons/plus"; import MapIcon from "@mui/icons-material/Map"; +import MapTab, { getTimestamp } from "../components/map/map-component"; const DecoderPage = () => { const { data: session } = useSession(); @@ -61,6 +58,7 @@ const DecoderPage = () => { [id]: { ...prevData[id], decodedResponse: response, + timestamp: getTimestampFromType(type, response), status: text == "" ? "NOT_STARTED" : response == undefined ? "ERROR" : "COMPLETED", }, }; @@ -112,25 +110,37 @@ const DecoderPage = () => { } }; + const getTimestampFromType = (type: DECODER_MESSAGE_TYPE, decodedResponse: DecoderApiResponseGeneric | undefined) => { + switch (type) { + case "MAP": + return getTimestamp(decodedResponse?.processedMap?.properties.odeReceivedAt); + case "SPAT": + return getTimestamp(decodedResponse?.processedSpat?.utcTimeStamp); + case "BSM": + return getTimestamp(decodedResponse?.bsm?.metadata.odeReceivedAt); + } + }; + const onFileUploaded = (contents: string[], type: DECODER_MESSAGE_TYPE) => { setData((prevData) => { const textToIds: { [text: string]: string } = {}; - contents.forEach((text) => + contents.forEach((text) => { + const id = uuidv4(); + textToIds[text] = id; submitDecoderRequest(text, type)?.then((response) => { - const id = uuidv4(); - textToIds[text] = id; setData((prevData) => { return { ...prevData, [id]: { - ...prevData[text], + ...prevData[id], decodedResponse: response, + timestamp: getTimestampFromType(type, response), status: text == "" ? "NOT_STARTED" : response == undefined ? "ERROR" : "COMPLETED", }, }; }); - }) - ); + }); + }); let newEntries = {}; contents.forEach((text) => { newEntries[textToIds[text]] = { @@ -138,6 +148,7 @@ const DecoderPage = () => { type: type, status: "IN_PROGRESS", text: text, + timestamp: undefined, selected: false, isGreyedOut: false, decodedResponse: undefined, @@ -150,6 +161,32 @@ const DecoderPage = () => { }); }; + const getIntersectionId = (decodedResponse: DecoderApiResponseGeneric | undefined) => { + if (!decodedResponse) { + return undefined; + } + + switch (decodedResponse.type) { + case "MAP": + const mapPayload = decodedResponse.processedMap; + return mapPayload?.properties?.intersectionId; + case "SPAT": + const spatPayload = decodedResponse.processedSpat; + return spatPayload?.intersectionId; + case "BSM": + const bsmPayload = decodedResponse.bsm; + return bsmPayload?.metadata.originIp; + } + }; + + const isGreyedOut = (intersectionId: number | undefined) => { + return selectedMapMessage?.intersectionId === undefined || intersectionId !== selectedMapMessage?.intersectionId; + }; + + const isGreyedOutIp = (rsuIp: string | undefined) => { + return (selectedMapMessage?.rsuIp === undefined || rsuIp !== selectedMapMessage?.rsuIp) && rsuIp != ""; + }; + return ( <> @@ -163,41 +200,57 @@ const DecoderPage = () => { py: 8, }} > - {/* + -
- - Query - -
+ + + + ASN.1 Decoder + + +
- - - -
*/} - - + v.type === "MAP" && v.status == "COMPLETED" && v.id == selectedMapMessage?.id) + .map((v) => v.decodedResponse?.processedMap!), + spat: Object.values(data) + .filter( + (v) => + v.type === "SPAT" && v.status == "COMPLETED" && !isGreyedOut(getIntersectionId(v.decodedResponse)) + ) + .map((v) => v.decodedResponse?.processedSpat!), + bsm: Object.values(data) + .filter( + (v) => + v.type === "BSM" && + v.status == "COMPLETED" && + !isGreyedOutIp(getIntersectionId(v.decodedResponse)) + ) + .map((v) => v.decodedResponse?.bsm!), + }} + sourceDataType={"exact"} + intersectionId={-1} + roadRegulatorId={-1} + /> + { /> - { - setOpenMapDialog(false); - }} - intersectionId={-1} - roadRegulatorId={-1} - map={Object.values(data) - .filter((v) => v.type === "MAP" && v.status == "COMPLETED") - .map((v) => v.decodedResponse?.processedMap!)} - spat={Object.values(data) - .filter((v) => v.type === "SPAT" && v.status == "COMPLETED") - .map((v) => v.decodedResponse?.processedSpat!)} - bsm={Object.values(data) - .filter((v) => v.type === "BSM" && v.status == "COMPLETED") - .map((v) => v.decodedResponse?.bsm!)} - /> ); }; From b325c9715a856082e1e5157c21c8fb174e160125 Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Wed, 29 May 2024 12:51:15 -0600 Subject: [PATCH 24/29] Final decoder visual updates --- gui/src/components/decoder/decoder-entry.tsx | 36 +++++++-- gui/src/components/decoder/decoder-tables.tsx | 44 +++++++++-- gui/src/components/map/map-component.tsx | 77 ++++++++----------- .../imports/J2735/J2735BsmCoreData.d.ts | 2 +- gui/src/pages/decoder.tsx | 57 +++++++++++--- 5 files changed, 148 insertions(+), 68 deletions(-) diff --git a/gui/src/components/decoder/decoder-entry.tsx b/gui/src/components/decoder/decoder-entry.tsx index fcc9da59..44f01598 100644 --- a/gui/src/components/decoder/decoder-entry.tsx +++ b/gui/src/components/decoder/decoder-entry.tsx @@ -131,23 +131,47 @@ export const DecoderEntry = (props: DecoderDataEntry & DecoderEntryProps) => { } }; + const getIconColor = () => { + switch (status) { + case "NOT_STARTED": + return "#555555"; + default: + return "#ffffff"; + } + }; + return ( - - {type == "MAP" && text != "" && } - {timestamp && {format(new Date(timestamp), "yyyy-MM-dd HH:mm:ss")}} + +
+ {(type == "MAP" || type == "BSM") && text != "" && ( + + )} + {timestamp && ( + + {format(new Date(timestamp), "yyyy-MM-dd HH:mm:ss")} + + )} +


- + - + {status === "IN_PROGRESS" && } diff --git a/gui/src/components/decoder/decoder-tables.tsx b/gui/src/components/decoder/decoder-tables.tsx index c2a13682..f634e152 100644 --- a/gui/src/components/decoder/decoder-tables.tsx +++ b/gui/src/components/decoder/decoder-tables.tsx @@ -1,5 +1,16 @@ import PerfectScrollbar from "react-perfect-scrollbar"; -import { Box, Card, IconButton, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from "@mui/material"; +import { + Box, + Card, + Checkbox, + IconButton, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, +} from "@mui/material"; import React from "react"; import { DecoderEntry } from "./decoder-entry"; import DownloadIcon from "@mui/icons-material/Download"; @@ -9,6 +20,8 @@ type DecoderTableProps = { selectedIntersectionId: number | undefined; selectedMapMessageId: string | undefined; selectedRsuIp: string | undefined; + selectedBsms: string[]; + setSelectedBsms: (bsms: string[]) => void; onItemSelected: (id: string) => void; onTextChanged: (id: string, messageText: string, type: DECODER_MESSAGE_TYPE) => void; onItemDeleted: (id: string) => void; @@ -21,6 +34,8 @@ export const DecoderTables = (props: DecoderTableProps) => { selectedIntersectionId, selectedMapMessageId, selectedRsuIp, + selectedBsms, + setSelectedBsms, onItemSelected, onTextChanged, onItemDeleted, @@ -89,6 +104,14 @@ export const DecoderTables = (props: DecoderTableProps) => { element.click(); }; + const toggleBsmSelection = () => { + if (selectedBsms.length == contents.filter((v) => v.type === "BSM").length) { + setSelectedBsms([]); + } else { + setSelectedBsms(contents.filter((v) => v.type === "BSM").map((v) => v.id)); + } + }; + return ( @@ -97,11 +120,11 @@ export const DecoderTables = (props: DecoderTableProps) => {
- + MAP Messages dataFileUploaded(event, "MAP")} title="Your custom description here" /> @@ -143,11 +166,11 @@ export const DecoderTables = (props: DecoderTableProps) => {
- + SPAT Messages dataFileUploaded(event, "SPAT")} title="Your custom description here" /> @@ -189,11 +212,16 @@ export const DecoderTables = (props: DecoderTableProps) => {
- + + v.type === "BSM").length} + onChange={toggleBsmSelection} + sx={{ m: 0, p: 0 }} + /> BSM Messages dataFileUploaded(event, "BSM")} title="Your custom description here" /> @@ -219,7 +247,7 @@ export const DecoderTables = (props: DecoderTableProps) => { text={entry.text} isGreyedOut={isGreyedOutIp(getIntersectionId(entry.decodedResponse))} decodedResponse={entry.decodedResponse} - selected={false} + selected={selectedBsms.includes(entry.id)} timestamp={entry.timestamp} onSelected={onItemSelected} onTextChanged={(id, text) => onTextChanged(id, text, "BSM")} diff --git a/gui/src/components/map/map-component.tsx b/gui/src/components/map/map-component.tsx index 3934086a..96cae04e 100644 --- a/gui/src/components/map/map-component.tsx +++ b/gui/src/components/map/map-component.tsx @@ -248,14 +248,6 @@ const generateQueryParams = ( let startDate = undefined as number | undefined; let endDate = undefined as number | undefined; - for (const map of (source as { map: ProcessedMap[] }).map) { - if (!startDate || getTimestamp(map.properties.odeReceivedAt) < startDate) { - startDate = getTimestamp(map.properties.odeReceivedAt); - } - if (!endDate || getTimestamp(map.properties.odeReceivedAt) > endDate) { - endDate = getTimestamp(map.properties.odeReceivedAt); - } - } for (const spat of (source as { spat: ProcessedSpat[] }).spat) { if (!startDate || getTimestamp(spat.utcTimeStamp) < startDate) { startDate = getTimestamp(spat.utcTimeStamp); @@ -264,14 +256,6 @@ const generateQueryParams = ( endDate = getTimestamp(spat.utcTimeStamp); } } - for (const bsm of (source as { bsm: OdeBsmData[] }).bsm) { - if (!startDate || getTimestamp(bsm.metadata.odeReceivedAt) < startDate) { - startDate = getTimestamp(bsm.metadata.odeReceivedAt); - } - if (!endDate || getTimestamp(bsm.metadata.odeReceivedAt) > endDate) { - endDate = getTimestamp(bsm.metadata.odeReceivedAt); - } - } return { startDate: new Date(startDate ?? Date.now()), endDate: new Date(endDate ?? Date.now() + 1), @@ -1327,36 +1311,41 @@ const MapTab = (props: MyProps) => { setSignalStateData(undefined); } - // retrieve filtered BSMs - const filteredBsms: BsmFeature[] = bsmData?.features?.filter( - (feature) => - feature.properties?.odeReceivedAt >= renderTimeInterval[0] && - feature.properties?.odeReceivedAt <= renderTimeInterval[1] - ); - const sortedBsms = filteredBsms.sort((a, b) => b.properties.odeReceivedAt - a.properties.odeReceivedAt); - // Update BSM legend colors - const uniqueIds = new Set(filteredBsms.map((bsm) => bsm.properties?.id).sort()); - const colors = generateColorDictionary(uniqueIds); - setMapLegendColors((prevValue) => ({ - ...prevValue, - bsmColors: colors, - })); - const bsmLayerStyle = generateMapboxStyleExpression(colors); - setBsmLayerStyle((prevValue) => ({ ...prevValue, paint: { ...prevValue.paint, "circle-color": bsmLayerStyle } })); - - const lastBsms: BsmFeature[] = []; - const bsmCounts: { [id: string]: number } = {}; - for (let i = 0; i < sortedBsms.length; i++) { - const id = sortedBsms[i].properties?.id; - if (bsmCounts[id] == undefined) { - bsmCounts[id] = 0; - } - if (bsmCounts[id] < bsmTrailLength) { - lastBsms.push(sortedBsms[i]); - bsmCounts[id]++; + if (props.sourceDataType == "exact") { + // In "exact" mode, always show all BSMs. Timestamps are meaningless. + setCurrentBsms(bsmData); + } else { + // retrieve filtered BSMs + const filteredBsms: BsmFeature[] = bsmData?.features?.filter( + (feature) => + feature.properties?.odeReceivedAt >= renderTimeInterval[0] && + feature.properties?.odeReceivedAt <= renderTimeInterval[1] + ); + const sortedBsms = filteredBsms.sort((a, b) => b.properties.odeReceivedAt - a.properties.odeReceivedAt); + // Update BSM legend colors + const uniqueIds = new Set(filteredBsms.map((bsm) => bsm.properties?.id).sort()); + const colors = generateColorDictionary(uniqueIds); + setMapLegendColors((prevValue) => ({ + ...prevValue, + bsmColors: colors, + })); + const bsmLayerStyle = generateMapboxStyleExpression(colors); + setBsmLayerStyle((prevValue) => ({ ...prevValue, paint: { ...prevValue.paint, "circle-color": bsmLayerStyle } })); + + const lastBsms: BsmFeature[] = []; + const bsmCounts: { [id: string]: number } = {}; + for (let i = 0; i < sortedBsms.length; i++) { + const id = sortedBsms[i].properties?.id; + if (bsmCounts[id] == undefined) { + bsmCounts[id] = 0; + } + if (bsmCounts[id] < bsmTrailLength) { + lastBsms.push(sortedBsms[i]); + bsmCounts[id]++; + } } + setCurrentBsms({ ...bsmData, features: lastBsms }); } - setCurrentBsms({ ...bsmData, features: lastBsms }); const filteredEvents: MessageMonitor.Event[] = surroundingEvents.filter( (event) => diff --git a/gui/src/models/imports/J2735/J2735BsmCoreData.d.ts b/gui/src/models/imports/J2735/J2735BsmCoreData.d.ts index fec7864b..53c60068 100644 --- a/gui/src/models/imports/J2735/J2735BsmCoreData.d.ts +++ b/gui/src/models/imports/J2735/J2735BsmCoreData.d.ts @@ -1,7 +1,7 @@ type J2735BsmCoreData = { msgCnt: number; id: string; - secMark: number; + secMark: number; // millisecond of minute? position: OdePosition3D; accelSet: J2735AccelerationSet4Way; diff --git a/gui/src/pages/decoder.tsx b/gui/src/pages/decoder.tsx index 4aa3c4bc..7c737bbb 100644 --- a/gui/src/pages/decoder.tsx +++ b/gui/src/pages/decoder.tsx @@ -17,14 +17,20 @@ const DecoderPage = () => { const [selectedMapMessage, setSelectedMapMessage] = useState( undefined as undefined | { id: string; intersectionId: number; rsuIp: string } ); + const [selectedBsms, setSelectedBsms] = useState([] as string[]); console.log("Data", data); useEffect(() => { const freshData = [] as DecoderDataEntry[]; for (let i = 0; i < 3; i++) { + const id = uuidv4(); + if (i % 3 == 2) { + // bsm + setSelectedBsms((prevBsms) => [...prevBsms, id]); + } freshData.push({ - id: uuidv4(), + id: id, type: i % 3 == 0 ? "MAP" : i % 3 == 1 ? "SPAT" : "BSM", status: "NOT_STARTED", text: "", @@ -52,6 +58,9 @@ const DecoderPage = () => { const onTextChanged = (id: string, text: string, type: DECODER_MESSAGE_TYPE) => { setData((prevData) => { submitDecoderRequest(text, type)?.then((response) => { + if (type == "BSM") { + setSelectedBsms((prevBsms) => [...prevBsms, id]); + } setData((prevData) => { return { ...prevData, @@ -103,10 +112,24 @@ const DecoderPage = () => { }; const onItemSelected = (id: string) => { - const intersectionId = data[id]?.decodedResponse?.processedMap?.properties?.intersectionId; - const rsuIp = data[id]?.decodedResponse?.processedMap?.properties?.originIp; - if (intersectionId) { - setSelectedMapMessage({ id, intersectionId, rsuIp: rsuIp! }); + const type = data[id].type; + switch (type) { + case "MAP": + const intersectionId = data[id]?.decodedResponse?.processedMap?.properties?.intersectionId; + const rsuIp = data[id]?.decodedResponse?.processedMap?.properties?.originIp; + if (intersectionId) { + setSelectedMapMessage({ id, intersectionId, rsuIp: rsuIp! }); + } + return; + case "BSM": + setSelectedBsms((prevBsms) => { + if (prevBsms.includes(id)) { + return prevBsms.filter((bsmId) => bsmId !== id); + } else { + return [...prevBsms, id]; + } + }); + return; } }; @@ -128,6 +151,9 @@ const DecoderPage = () => { const id = uuidv4(); textToIds[text] = id; submitDecoderRequest(text, type)?.then((response) => { + if (type == "BSM") { + setSelectedBsms((prevBsms) => [...prevBsms, id]); + } setData((prevData) => { return { ...prevData, @@ -197,10 +223,10 @@ const DecoderPage = () => { sx={{ backgroundColor: "background.default", flexGrow: 1, - py: 8, + py: 4, }} > - + { > - + ASN.1 Decoder @@ -251,8 +277,19 @@ const DecoderPage = () => { roadRegulatorId={-1} /> + + + + 1. Upload data, either by uploading individual files or pasting the data directly into the text box. +
+ 2. Select an uploaded MAP message to view the decoded data. SPAT data is filtered by intersection ID. +
+ 3. Select BSM messages to view the decoded data. All selected BSM data is shown. +
+
+
- + { onTextChanged={onTextChanged} onItemDeleted={onItemDeleted} onFileUploaded={onFileUploaded} + selectedBsms={selectedBsms} + setSelectedBsms={setSelectedBsms} /> From 5e9da3672048c606b292e39e200c7beb69437354 Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Wed, 29 May 2024 12:54:56 -0600 Subject: [PATCH 25/29] Filtering out unselected BSMs --- gui/src/components/decoder/decoder-tables.tsx | 6 +----- gui/src/pages/decoder.tsx | 7 +------ 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/gui/src/components/decoder/decoder-tables.tsx b/gui/src/components/decoder/decoder-tables.tsx index f634e152..d371669b 100644 --- a/gui/src/components/decoder/decoder-tables.tsx +++ b/gui/src/components/decoder/decoder-tables.tsx @@ -63,10 +63,6 @@ export const DecoderTables = (props: DecoderTableProps) => { return selectedIntersectionId === undefined || intersectionId !== selectedIntersectionId; }; - const isGreyedOutIp = (rsuIp: string | undefined) => { - return (selectedRsuIp === undefined || rsuIp !== selectedRsuIp) && rsuIp != ""; - }; - const dataFileUploaded = (event, type: DECODER_MESSAGE_TYPE) => { const file = event.target.files[0]; if (file) { @@ -245,7 +241,7 @@ export const DecoderTables = (props: DecoderTableProps) => { status={entry.status} type={entry.type} text={entry.text} - isGreyedOut={isGreyedOutIp(getIntersectionId(entry.decodedResponse))} + isGreyedOut={false} decodedResponse={entry.decodedResponse} selected={selectedBsms.includes(entry.id)} timestamp={entry.timestamp} diff --git a/gui/src/pages/decoder.tsx b/gui/src/pages/decoder.tsx index 7c737bbb..9013840f 100644 --- a/gui/src/pages/decoder.tsx +++ b/gui/src/pages/decoder.tsx @@ -264,12 +264,7 @@ const DecoderPage = () => { ) .map((v) => v.decodedResponse?.processedSpat!), bsm: Object.values(data) - .filter( - (v) => - v.type === "BSM" && - v.status == "COMPLETED" && - !isGreyedOutIp(getIntersectionId(v.decodedResponse)) - ) + .filter((v) => v.type === "BSM" && v.status == "COMPLETED" && v.selected) .map((v) => v.decodedResponse?.bsm!), }} sourceDataType={"exact"} From af7bfd466559e217f37028612f27649ca02d4caf Mon Sep 17 00:00:00 2001 From: John-Wiens Date: Thu, 30 May 2024 17:29:38 -0600 Subject: [PATCH 26/29] Fixed Broken Dockerfile after merge --- api/Dockerfile | 138 ++----------------------------------------------- 1 file changed, 5 insertions(+), 133 deletions(-) diff --git a/api/Dockerfile b/api/Dockerfile index a9cb7d6b..651f1e17 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -1,69 +1,3 @@ -# FROM maven:3.8-eclipse-temurin-21-alpine as builder - -# WORKDIR /home - -# # Copy only the files needed to avoid putting all sorts of junk from your local env on to the image -# COPY ./jpo-conflictmonitor/jpo-geojsonconverter/jpo-ode/pom.xml ./jpo-ode/ -# COPY ./jpo-conflictmonitor/jpo-geojsonconverter/jpo-ode/jpo-ode-common/pom.xml ./jpo-ode/jpo-ode-common/ -# COPY ./jpo-conflictmonitor/jpo-geojsonconverter/jpo-ode/jpo-ode-common/src ./jpo-ode/jpo-ode-common/src -# COPY ./jpo-conflictmonitor/jpo-geojsonconverter/jpo-ode/jpo-ode-plugins/pom.xml ./jpo-ode/jpo-ode-plugins/ -# COPY ./jpo-conflictmonitor/jpo-geojsonconverter/jpo-ode/jpo-ode-plugins/src ./jpo-ode/jpo-ode-plugins/src -# COPY ./jpo-conflictmonitor/jpo-geojsonconverter/jpo-ode/jpo-ode-core/pom.xml ./jpo-ode/jpo-ode-core/ -# COPY ./jpo-conflictmonitor/jpo-geojsonconverter/jpo-ode/jpo-ode-core/src ./jpo-ode/jpo-ode-core/src/ -# COPY ./jpo-conflictmonitor/jpo-geojsonconverter/jpo-ode/jpo-ode-svcs/pom.xml ./jpo-ode/jpo-ode-svcs/ -# COPY ./jpo-conflictmonitor/jpo-geojsonconverter/jpo-ode/jpo-ode-svcs/src ./jpo-ode/jpo-ode-svcs/src - -# COPY ./jpo-conflictmonitor/jpo-geojsonconverter/jpo-geojsonconverter/pom.xml ./jpo-geojsonconverter/ -# COPY ./jpo-conflictmonitor/jpo-geojsonconverter/jpo-geojsonconverter/src ./jpo-geojsonconverter/src - -# COPY ./jpo-conflictmonitor/jpo-conflictmonitor/pom.xml ./jpo-conflictmonitor/ -# COPY ./jpo-conflictmonitor/jpo-conflictmonitor/src ./jpo-conflictmonitor/src - -# COPY ./jpo-conflictvisualizer-api/pom.xml ./jpo-conflictvisualizer-api/ -# COPY ./jpo-conflictvisualizer-api/src ./jpo-conflictvisualizer-api/src - -# WORKDIR /home/jpo-ode - -# RUN mvn install -DskipTests - -# WORKDIR /home/jpo-geojsonconverter - -# RUN mvn clean install -DskipTests - -# WORKDIR /home/jpo-conflictmonitor - -# RUN mvn clean install -DskipTests - -# WORKDIR /home/jpo-conflictvisualizer-api - -# RUN mvn clean package -DskipTests -# # ENTRYPOINT ["tail", "-f", "/dev/null"] -# FROM eclipse-temurin:21-jre-alpine - -# WORKDIR /home - -# COPY --from=builder /home/jpo-conflictvisualizer-api/src/main/resources/application.yaml /home -# COPY --from=builder /home/jpo-conflictvisualizer-api/src/main/resources/logback.xml /home -# COPY --from=builder /home/jpo-conflictvisualizer-api/target/jpo-conflictvisualizer-api-0.0.1-SNAPSHOT.jar /home - -# #COPY cert.crt /home/cert.crt -# #RUN keytool -import -trustcacerts -keystore /opt/java/openjdk/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-conflictvisualizer-api-0.0.1-SNAPSHOT.jar"] - -# # ENTRYPOINT ["tail", "-f", "/dev/null"] - - # === BUILDER IMAGE for ACM === # FROM alpine:3.12 as builder FROM amazonlinux:2023 as builder @@ -153,7 +87,7 @@ RUN mvn clean install -DskipTests WORKDIR /home/jpo-conflictvisualizer-api RUN mvn clean package -DskipTests -# # ENTRYPOINT ["tail", "-f", "/dev/null"] +ENTRYPOINT ["tail", "-f", "/dev/null"] # # === RUNTIME IMAGE for Java and ACM === @@ -163,10 +97,9 @@ FROM amazoncorretto:21-al2023 WORKDIR /home -COPY --from=builder /home/jpo-conflictvisualizer-api/src/main/resources/application.yaml /home -COPY --from=builder /home/jpo-conflictvisualizer-api/src/main/resources/logback.xml /home -COPY --from=builder /home/jpo-conflictvisualizer-api/target/jpo-conflictvisualizer-api-1.3.0-SNAPSHOT.jar /home - +COPY --from=jbuilder /home/jpo-conflictvisualizer-api/src/main/resources/application.yaml /home +COPY --from=jbuilder /home/jpo-conflictvisualizer-api/src/main/resources/logback.xml /home +COPY --from=jbuilder /home/jpo-conflictvisualizer-api/target/jpo-conflictvisualizer-api-1.3.0-SNAPSHOT.jar /home # # Copy asn1_codec executable and test files USER root WORKDIR /asn1_codec @@ -194,67 +127,6 @@ ADD ./asn1_codec/docker-test /asn1_codec/docker-test WORKDIR /home -# # Install Java 21, not available from apk in Alpine 3.12 -# # Dockerfile for eclipse-temurin:21-jre-alpine copied from: -# # https://github.com/adoptium/containers/blob/057e5aa7581e96b8a2334290e750b329d62abfdf/21/jre/alpine/Dockerfile - -# ENV JAVA_HOME /opt/java/openjdk -# ENV PATH $JAVA_HOME/bin:$PATH - -# # Default to UTF-8 file.encoding -# ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' LC_ALL='en_US.UTF-8' - -# RUN set -eux; \ -# apk add --no-cache \ -# # java.lang.UnsatisfiedLinkError: libfontmanager.so: libfreetype.so.6: cannot open shared object file: No such file or directory -# # java.lang.NoClassDefFoundError: Could not initialize class sun.awt.X11FontManager -# # https://github.com/docker-library/openjdk/pull/235#issuecomment-424466077 -# fontconfig ttf-dejavu \ -# # utilities for keeping Alpine and OpenJDK CA certificates in sync -# # https://github.com/adoptium/containers/issues/293 -# ca-certificates p11-kit-trust \ -# # locales ensures proper character encoding and locale-specific behaviors using en_US.UTF-8 -# musl-locales musl-locales-lang \ -# tzdata \ -# ; \ -# rm -rf /var/cache/apk/* - -# ENV JAVA_VERSION jdk-21.0.3+9 - -# RUN set -eux; \ -# ARCH="$(apk --print-arch)"; \ -# case "${ARCH}" in \ -# aarch64|arm64) \ -# ESUM='54e8618da373258654fe788d509f087d3612de9e080eb6831601069dbc8a4b2b'; \ -# BINARY_URL='https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.3%2B9/OpenJDK21U-jre_aarch64_alpine-linux_hotspot_21.0.3_9.tar.gz'; \ -# ;; \ -# amd64|x86_64) \ -# ESUM='b3e7170deab11a7089fe8e14f9f398424fd86db085f745dad212f6cfc4121df6'; \ -# BINARY_URL='https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.3%2B9/OpenJDK21U-jre_x64_alpine-linux_hotspot_21.0.3_9.tar.gz'; \ -# ;; \ -# *) \ -# echo "Unsupported arch: ${ARCH}"; \ -# exit 1; \ -# ;; \ -# esac; \ -# wget -O /tmp/openjdk.tar.gz ${BINARY_URL}; \ -# echo "${ESUM} */tmp/openjdk.tar.gz" | sha256sum -c -; \ -# mkdir -p "$JAVA_HOME"; \ -# tar --extract \ -# --file /tmp/openjdk.tar.gz \ -# --directory "$JAVA_HOME" \ -# --strip-components 1 \ -# --no-same-owner \ -# ; \ -# rm -f /tmp/openjdk.tar.gz ${JAVA_HOME}/lib/src.zip; - -# RUN set -eux; \ -# echo "Verifying install ..."; \ -# echo "java --version"; java --version; \ -# echo "Complete." - - - # # ENTRYPOINT ["java", "-jar", "/home/asn1-codec-java.jar"] # # #COPY cert.crt /home/cert.crt # # #RUN keytool -import -trustcacerts -keystore /opt/java/openjdk/lib/security/cacerts -storepass changeit -noprompt -alias mycert -file cert.crt @@ -269,4 +141,4 @@ ENTRYPOINT ["java", \ "-Dcom.sun.management.jmxremote.ssl=false", \ "-Dlogback.configurationFile=/home/logback.xml", \ "-jar", \ - "/home/jpo-conflictvisualizer-api-0.0.1-SNAPSHOT.jar"] \ No newline at end of file + "/home/jpo-conflictvisualizer-api-1.3.0-SNAPSHOT.jar"] \ No newline at end of file From e78e82d3dc414135f014b9e7bf26c8a6ca384c51 Mon Sep 17 00:00:00 2001 From: John-Wiens Date: Thu, 30 May 2024 17:51:06 -0600 Subject: [PATCH 27/29] Updating Conflict Monitor Submodule --- api/jpo-conflictvisualizer-api/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/api/jpo-conflictvisualizer-api/pom.xml b/api/jpo-conflictvisualizer-api/pom.xml index 7bcc84c1..3b9a692c 100644 --- a/api/jpo-conflictvisualizer-api/pom.xml +++ b/api/jpo-conflictvisualizer-api/pom.xml @@ -13,6 +13,7 @@ jpo-conflictvisualizer-api 1.3.0-SNAPSHOT jpo-conflictvisualizer-api + jar Conflict Visualizer 21 From 891fae49aae935438ffef0dbd44bd79c21ec7c7d Mon Sep 17 00:00:00 2001 From: John-Wiens Date: Fri, 31 May 2024 09:03:46 -0600 Subject: [PATCH 28/29] Fixed Broken Unit Tests from Updated GeoJson Converter --- .../us/dot/its/jpo/ode/api/decoderTests/MapDecoderTests.java | 2 +- .../us/dot/its/jpo/ode/api/decoderTests/SpatDecoderTests.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/decoderTests/MapDecoderTests.java b/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/decoderTests/MapDecoderTests.java index b7a4647d..80712abe 100644 --- a/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/decoderTests/MapDecoderTests.java +++ b/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/decoderTests/MapDecoderTests.java @@ -32,7 +32,7 @@ public class MapDecoderTests { private String odeMapDecodedXmlReference = "mapTxsuccessunsecuredDataMessageFrameUPERus.dot.its.jpo.ode.model.OdeAsn1Payloadaf11b4ee-e2eb-48ef-82c6-97172b6cda7a10002024-05-10T15:57:46.531370277Z60falseRSU172.18.0.1MessageFrame1800121100395952649-10509141221677036621100000000000000000001000000000002225808-1010517-161-602769112-106142-180-306636-12-203804-7191000000000004131100000000000000000001000000000002222515-102933-82-106259-85-403416-98-206867-39-302657-55-10425967-1034810181000000000004111100000000000000000000010000000002215121810451-223-701852-108-1028001000000000141100000000000000000000100000000002213-124-10718720-4054140-101298229120100000000007162010000000000000000002267-1019-10365752010000000000000000002267-662-10338072010000000000000000002294-1463-103652712401000000000000000000-952-18980-37713401000000000000000000-1440-18987-390000710310000000000000000000100000000000705-1919-1-7515-40-41-527210-17-1133717-59381000-7-7261000000000006193100000000000000000001000000000001090-1930-109-4764-10-20-6402-20-34-7953-1016-1094020271000000000006111310000000000000000000010000000000350-1925-32-6698-30156-2616180100000000001183100000000000000000000010000000001557-1956-10-101-1956-5-7393-40-121-16367001000000000115510000000000000000000100000000000-2405-101810-93392630-11895-3850-88141361000000000008116510000000000000000000100000000000-2337-65610-122291550-11362-3730-6537151000000000008117510000000000000000000010000000000-2338-28410-8892-1830-11959-250-1564-180260100000000003114510000000000000000000001000000000-2401-138010-19947880-10108-713001000000000118601000000000000000000-226447920-580-1419601000000000000000000-225779620-518020601000000000000000000-2236122920-484023710000000000000000000100000000000-9141903202251228830409634-20328035-3000-728121000000000002122710000000000000000000100000000000-12211914209210475308911582-20487912-30131000000000002121710000000000000000000001000000000-1649193520163182102159912010248108511803-30194161-2020001000000000124710000000000000000000010000000000-5351926202845821088379310-14318661060100000000005125710000000000000000000010000000000-21319132014459410-9122381050100000000005126801000000000000000000724190210-735210278010000000000000000001105188810-733210288010000000000000000001591187410-1434607"; private String odeMapDecodedDataReference = "{\"metadata\":{\"logFileName\":\"\",\"recordType\":\"mapTx\",\"securityResultCode\":\"success\",\"receivedMessageDetails\":{\"rxSource\":\"NA\"},\"payloadType\":\"us.dot.its.jpo.ode.model.OdeMapPayload\",\"serialId\":{\"streamId\":\"af11b4ee-e2eb-48ef-82c6-97172b6cda7a\",\"bundleSize\":1,\"bundleId\":0,\"recordId\":0,\"serialNumber\":0},\"odeReceivedAt\":\"2024-05-10T15:57:46.531370277Z\",\"schemaVersion\":6,\"maxDurationTime\":0,\"recordGeneratedAt\":\"\",\"sanitized\":false,\"odePacketID\":\"\",\"odeTimStartDateTime\":\"\",\"mapSource\":\"RSU\",\"originIp\":\"172.18.0.1\"},\"payload\":{\"data\":{\"msgIssueRevision\":0,\"layerType\":\"intersectionData\",\"layerID\":0,\"intersections\":{\"intersectionGeometry\":[{\"id\":{\"id\":12110},\"revision\":0,\"refPoint\":{\"latitude\":39.5952649,\"longitude\":-105.0914122,\"elevation\":1677.0},\"laneWidth\":366,\"laneSet\":{\"GenericLane\":[{\"laneID\":2,\"ingressApproach\":1,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":2225,\"y\":808}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":10517,\"y\":-161}},\"attributes\":{\"dElevation\":-60}},{\"delta\":{\"nodeXY6\":{\"x\":2769,\"y\":112}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":6142,\"y\":-180}},\"attributes\":{\"dElevation\":-30}},{\"delta\":{\"nodeXY6\":{\"x\":6636,\"y\":-12}},\"attributes\":{\"dElevation\":-20}},{\"delta\":{\"nodeXY6\":{\"x\":3804,\"y\":-7}}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":19,\"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\":4,\"connectionID\":1}]}},{\"laneID\":3,\"ingressApproach\":1,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":2222,\"y\":515}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":2933,\"y\":-82}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":6259,\"y\":-85}},\"attributes\":{\"dElevation\":-40}},{\"delta\":{\"nodeXY6\":{\"x\":3416,\"y\":-98}},\"attributes\":{\"dElevation\":-20}},{\"delta\":{\"nodeXY6\":{\"x\":6867,\"y\":-39}},\"attributes\":{\"dElevation\":-30}},{\"delta\":{\"nodeXY6\":{\"x\":2657,\"y\":-55}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":4259,\"y\":67}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":3481,\"y\":0}}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":18,\"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\":4,\"connectionID\":1}]}},{\"laneID\":1,\"ingressApproach\":1,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":2215,\"y\":1218}}},{\"delta\":{\"nodeXY6\":{\"x\":10451,\"y\":-223}},\"attributes\":{\"dElevation\":-70}},{\"delta\":{\"nodeXY6\":{\"x\":1852,\"y\":-108}},\"attributes\":{\"dElevation\":-10}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":28,\"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}]}},{\"laneID\":4,\"ingressApproach\":1,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":2213,\"y\":-124}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":7187,\"y\":20}},\"attributes\":{\"dElevation\":-40}},{\"delta\":{\"nodeXY6\":{\"x\":541,\"y\":40}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":1298,\"y\":229}}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":12,\"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\":7,\"connectionID\":1}]}},{\"laneID\":6,\"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}}},\"nodeList\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":2267,\"y\":-1019}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":365,\"y\":7}}}]}}},{\"laneID\":5,\"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}}},\"nodeList\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":2267,\"y\":-662}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":338,\"y\":0}}}]}}},{\"laneID\":7,\"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}}},\"nodeList\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":2294,\"y\":-1463}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":365,\"y\":27}}}]}}},{\"laneID\":12,\"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}}},\"nodeList\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-952,\"y\":-1898}}},{\"delta\":{\"nodeXY6\":{\"x\":0,\"y\":-377}}}]}}},{\"laneID\":13,\"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}}},\"nodeList\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-1440,\"y\":-1898}}},{\"delta\":{\"nodeXY6\":{\"x\":7,\"y\":-390}}},{\"delta\":{\"nodeXY6\":{\"x\":0,\"y\":0}}},{\"delta\":{\"nodeXY6\":{\"x\":0,\"y\":7}}}]}}},{\"laneID\":10,\"ingressApproach\":3,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":705,\"y\":-1919}}},{\"delta\":{\"nodeXY6\":{\"x\":-1,\"y\":-7515}},\"attributes\":{\"dElevation\":-40}},{\"delta\":{\"nodeXY6\":{\"x\":-41,\"y\":-5272}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":-17,\"y\":-11337}}},{\"delta\":{\"nodeXY6\":{\"x\":17,\"y\":-5938}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":0,\"y\":0}}},{\"delta\":{\"nodeXY6\":{\"x\":-7,\"y\":-7}}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":26,\"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}]}},{\"laneID\":9,\"ingressApproach\":3,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":1090,\"y\":-1930}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":9,\"y\":-4764}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":-20,\"y\":-6402}},\"attributes\":{\"dElevation\":-20}},{\"delta\":{\"nodeXY6\":{\"x\":-34,\"y\":-7953}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":16,\"y\":-10940}},\"attributes\":{\"dElevation\":20}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":27,\"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}]}},{\"laneID\":11,\"ingressApproach\":3,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":350,\"y\":-1925}}},{\"delta\":{\"nodeXY6\":{\"x\":-32,\"y\":-6698}},\"attributes\":{\"dElevation\":-30}},{\"delta\":{\"nodeXY6\":{\"x\":156,\"y\":-2616}}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":18,\"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\":1,\"connectionID\":1}]}},{\"laneID\":8,\"ingressApproach\":3,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":1557,\"y\":-1956}},\"attributes\":{\"dElevation\":-10}},{\"delta\":{\"nodeXY6\":{\"x\":-101,\"y\":-1956}}},{\"delta\":{\"nodeXY6\":{\"x\":-5,\"y\":-7393}},\"attributes\":{\"dElevation\":-40}},{\"delta\":{\"nodeXY6\":{\"x\":-121,\"y\":-1636}}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":7,\"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}]}},{\"laneID\":15,\"ingressApproach\":5,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-2405,\"y\":-1018}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":-9339,\"y\":26}},\"attributes\":{\"dElevation\":30}},{\"delta\":{\"nodeXY6\":{\"x\":-11895,\"y\":-38}},\"attributes\":{\"dElevation\":50}},{\"delta\":{\"nodeXY6\":{\"x\":-8814,\"y\":13}}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":6,\"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\":8,\"connectionID\":1}]}},{\"laneID\":16,\"ingressApproach\":5,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-2337,\"y\":-656}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":-12229,\"y\":15}},\"attributes\":{\"dElevation\":50}},{\"delta\":{\"nodeXY6\":{\"x\":-11362,\"y\":-37}},\"attributes\":{\"dElevation\":30}},{\"delta\":{\"nodeXY6\":{\"x\":-6537,\"y\":1}}}]}},\"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}},\"signalGroup\":8,\"connectionID\":1}]}},{\"laneID\":17,\"ingressApproach\":5,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-2338,\"y\":-284}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":-8892,\"y\":-18}},\"attributes\":{\"dElevation\":30}},{\"delta\":{\"nodeXY6\":{\"x\":-11959,\"y\":-2}},\"attributes\":{\"dElevation\":50}},{\"delta\":{\"nodeXY6\":{\"x\":-1564,\"y\":-180}}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":26,\"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\":3,\"connectionID\":1}]}},{\"laneID\":14,\"ingressApproach\":5,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-2401,\"y\":-1380}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":-19947,\"y\":8}},\"attributes\":{\"dElevation\":80}},{\"delta\":{\"nodeXY6\":{\"x\":-10108,\"y\":-7}}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":13,\"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}]}},{\"laneID\":18,\"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}}},\"nodeList\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-2264,\"y\":479}},\"attributes\":{\"dElevation\":20}},{\"delta\":{\"nodeXY6\":{\"x\":-580,\"y\":-14}}}]}}},{\"laneID\":19,\"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}}},\"nodeList\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-2257,\"y\":796}},\"attributes\":{\"dElevation\":20}},{\"delta\":{\"nodeXY6\":{\"x\":-518,\"y\":0}}}]}}},{\"laneID\":20,\"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}}},\"nodeList\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-2236,\"y\":1229}},\"attributes\":{\"dElevation\":20}},{\"delta\":{\"nodeXY6\":{\"x\":-484,\"y\":0}}}]}}},{\"laneID\":23,\"ingressApproach\":7,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-914,\"y\":1903}},\"attributes\":{\"dElevation\":20}},{\"delta\":{\"nodeXY6\":{\"x\":225,\"y\":12288}},\"attributes\":{\"dElevation\":30}},{\"delta\":{\"nodeXY6\":{\"x\":40,\"y\":9634}},\"attributes\":{\"dElevation\":-20}},{\"delta\":{\"nodeXY6\":{\"x\":32,\"y\":8035}},\"attributes\":{\"dElevation\":-30}},{\"delta\":{\"nodeXY6\":{\"x\":0,\"y\":0}}},{\"delta\":{\"nodeXY6\":{\"x\":-7,\"y\":28}}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":12,\"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}]}},{\"laneID\":22,\"ingressApproach\":7,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-1221,\"y\":1914}},\"attributes\":{\"dElevation\":20}},{\"delta\":{\"nodeXY6\":{\"x\":92,\"y\":10475}},\"attributes\":{\"dElevation\":30}},{\"delta\":{\"nodeXY6\":{\"x\":89,\"y\":11582}},\"attributes\":{\"dElevation\":-20}},{\"delta\":{\"nodeXY6\":{\"x\":48,\"y\":7912}},\"attributes\":{\"dElevation\":-30}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":13,\"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}]}},{\"laneID\":21,\"ingressApproach\":7,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-1649,\"y\":1935}},\"attributes\":{\"dElevation\":20}},{\"delta\":{\"nodeXY6\":{\"x\":16,\"y\":3182}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":21,\"y\":5991}},\"attributes\":{\"dElevation\":20}},{\"delta\":{\"nodeXY6\":{\"x\":102,\"y\":4810}}},{\"delta\":{\"nodeXY6\":{\"x\":85,\"y\":11803}},\"attributes\":{\"dElevation\":-30}},{\"delta\":{\"nodeXY6\":{\"x\":19,\"y\":4161}},\"attributes\":{\"dElevation\":-20}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":20,\"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}]}},{\"laneID\":24,\"ingressApproach\":7,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-535,\"y\":1926}},\"attributes\":{\"dElevation\":20}},{\"delta\":{\"nodeXY6\":{\"x\":28,\"y\":4582}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":88,\"y\":3793}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":-143,\"y\":1866}},\"attributes\":{\"dElevation\":10}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":6,\"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\":5,\"connectionID\":1}]}},{\"laneID\":25,\"ingressApproach\":7,\"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}}},\"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\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":-213,\"y\":1913}},\"attributes\":{\"dElevation\":20}},{\"delta\":{\"nodeXY6\":{\"x\":14,\"y\":4594}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":-91,\"y\":2238}},\"attributes\":{\"dElevation\":10}}]}},\"connectsTo\":{\"connectsTo\":[{\"connectingLane\":{\"lane\":5,\"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\":5,\"connectionID\":1}]}},{\"laneID\":26,\"egressApproach\":8,\"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}}},\"nodeList\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":724,\"y\":1902}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":-7,\"y\":352}},\"attributes\":{\"dElevation\":10}}]}}},{\"laneID\":27,\"egressApproach\":8,\"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}}},\"nodeList\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":1105,\"y\":1888}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":-7,\"y\":332}},\"attributes\":{\"dElevation\":10}}]}}},{\"laneID\":28,\"egressApproach\":8,\"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}}},\"nodeList\":{\"nodes\":{\"NodeXY\":[{\"delta\":{\"nodeXY6\":{\"x\":1591,\"y\":1874}},\"attributes\":{\"dElevation\":10}},{\"delta\":{\"nodeXY6\":{\"x\":-14,\"y\":346}}},{\"delta\":{\"nodeXY6\":{\"x\":0,\"y\":7}}}]}}}]}}]}},\"dataType\":\"us.dot.its.jpo.ode.plugin.j2735.J2735MAP\"}}"; - private String processedMapDataReference = "{\"mapFeatureCollection\":{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"id\":2,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911523,39.5953376],[-105.0899239,39.5953231],[-105.0896005,39.5953332],[-105.0888831,39.595317],[-105.088108,39.5953159],[-105.0876637,39.5953153]]},\"properties\":{\"nodes\":[{\"delta\":[2225,808],\"delevation\":-10},{\"delta\":[10517,-161],\"delevation\":-60},{\"delta\":[2769,112],\"delevation\":-10},{\"delta\":[6142,-180],\"delevation\":-30},{\"delta\":[6636,-12],\"delevation\":-20},{\"delta\":[3804,-7]}],\"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\":19,\"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\":4,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":3,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911527,39.5953113],[-105.0908101,39.5953039],[-105.0900791,39.5952962],[-105.0896801,39.5952874],[-105.0888781,39.5952839],[-105.0885678,39.5952789],[-105.0880704,39.5952849],[-105.0876638,39.5952849]]},\"properties\":{\"nodes\":[{\"delta\":[2222,515],\"delevation\":-10},{\"delta\":[2933,-82],\"delevation\":-10},{\"delta\":[6259,-85],\"delevation\":-40},{\"delta\":[3416,-98],\"delevation\":-20},{\"delta\":[6867,-39],\"delevation\":-30},{\"delta\":[2657,-55],\"delevation\":-10},{\"delta\":[4259,67],\"delevation\":-10},{\"delta\":[3481,0]}],\"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\":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\":18,\"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\":4,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":1,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911535,39.5953745],[-105.0899328,39.5953544],[-105.0897165,39.5953447]]},\"properties\":{\"nodes\":[{\"delta\":[2215,1218]},{\"delta\":[10451,-223],\"delevation\":-70},{\"delta\":[1852,-108],\"delevation\":-10}],\"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\":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\":28,\"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\":4,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911537,39.5952537],[-105.0903143,39.5952555],[-105.0902511,39.5952591],[-105.0900995,39.5952797]]},\"properties\":{\"nodes\":[{\"delta\":[2213,-124],\"delevation\":-10},{\"delta\":[7187,20],\"delevation\":-40},{\"delta\":[541,40],\"delevation\":-10},{\"delta\":[1298,229]}],\"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\":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\":12,\"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\":7,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":6,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911474,39.5951732],[-105.0911048,39.5951738]]},\"properties\":{\"nodes\":[{\"delta\":[2267,-1019],\"delevation\":-10},{\"delta\":[365,7]}],\"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.0911474,39.5952053],[-105.0911079,39.5952053]]},\"properties\":{\"nodes\":[{\"delta\":[2267,-662],\"delevation\":-10},{\"delta\":[338,0]}],\"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\":7,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911443,39.5951332],[-105.0911017,39.5951356]]},\"properties\":{\"nodes\":[{\"delta\":[2294,-1463],\"delevation\":-10},{\"delta\":[365,27]}],\"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\":2,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":12,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0915234,39.5950941],[-105.0915234,39.5950602]]},\"properties\":{\"nodes\":[{\"delta\":[-952,-1898]},{\"delta\":[0,-377]}],\"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\":4,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":13,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0915804,39.5950941],[-105.0915796,39.595059],[-105.0915796,39.595059],[-105.0915796,39.5950596]]},\"properties\":{\"nodes\":[{\"delta\":[-1440,-1898]},{\"delta\":[7,-390]},{\"delta\":[0,0]},{\"delta\":[0,7]}],\"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\":4,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":10,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0913299,39.5950922],[-105.09133,39.5944158],[-105.0913348,39.5939413],[-105.0913368,39.592921],[-105.0913348,39.5923866],[-105.0913348,39.5923866],[-105.0913356,39.592386]]},\"properties\":{\"nodes\":[{\"delta\":[705,-1919]},{\"delta\":[-1,-7515],\"delevation\":-40},{\"delta\":[-41,-5272],\"delevation\":10},{\"delta\":[-17,-11337]},{\"delta\":[17,-5938],\"delevation\":10},{\"delta\":[0,0]},{\"delta\":[-7,-7]}],\"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\":0,\"ingressApproach\":3,\"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\":26,\"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\":9,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0912849,39.5950912],[-105.0912838,39.5946624],[-105.0912861,39.5940862],[-105.0912901,39.5933704],[-105.0912882,39.5923858]]},\"properties\":{\"nodes\":[{\"delta\":[1090,-1930],\"delevation\":-10},{\"delta\":[9,-4764],\"delevation\":-10},{\"delta\":[-20,-6402],\"delevation\":-20},{\"delta\":[-34,-7953],\"delevation\":-10},{\"delta\":[16,-10940],\"delevation\":20}],\"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\":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\":27,\"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.0913713,39.5950916],[-105.091375,39.5944888],[-105.0913568,39.5942534]]},\"properties\":{\"nodes\":[{\"delta\":[350,-1925]},{\"delta\":[-32,-6698],\"delevation\":-30},{\"delta\":[156,-2616]}],\"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\":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\":18,\"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\":1,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":8,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0912303,39.5950889],[-105.0912421,39.5949129],[-105.0912427,39.5942475],[-105.0912568,39.5941003]]},\"properties\":{\"nodes\":[{\"delta\":[1557,-1956],\"delevation\":-10},{\"delta\":[-101,-1956]},{\"delta\":[-5,-7393],\"delevation\":-40},{\"delta\":[-121,-1636]}],\"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\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":7,\"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\":15,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916931,39.5951733],[-105.0927839,39.5951756],[-105.0941732,39.5951722],[-105.0952027,39.5951734]]},\"properties\":{\"nodes\":[{\"delta\":[-2405,-1018],\"delevation\":10},{\"delta\":[-9339,26],\"delevation\":30},{\"delta\":[-11895,-38],\"delevation\":50},{\"delta\":[-8814,13]}],\"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\":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\":6,\"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\":8,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":16,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916852,39.5952059],[-105.0931135,39.5952073],[-105.0944406,39.595204],[-105.0952041,39.5952041]]},\"properties\":{\"nodes\":[{\"delta\":[-2337,-656],\"delevation\":10},{\"delta\":[-12229,15],\"delevation\":50},{\"delta\":[-11362,-37],\"delevation\":30},{\"delta\":[-6537,1]}],\"laneId\":16,\"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\":8,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":17,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916853,39.5952393],[-105.0927239,39.5952377],[-105.0941207,39.5952375],[-105.0943034,39.5952213]]},\"properties\":{\"nodes\":[{\"delta\":[-2338,-284],\"delevation\":10},{\"delta\":[-8892,-18],\"delevation\":30},{\"delta\":[-11959,-2],\"delevation\":50},{\"delta\":[-1564,-180]}],\"laneId\":17,\"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\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":26,\"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\":3,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":14,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916926,39.5951407],[-105.0940224,39.5951414],[-105.095203,39.5951408]]},\"properties\":{\"nodes\":[{\"delta\":[-2401,-1380],\"delevation\":10},{\"delta\":[-19947,8],\"delevation\":80},{\"delta\":[-10108,-7]}],\"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\":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\":13,\"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\":18,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916766,39.595308],[-105.0917443,39.5953067]]},\"properties\":{\"nodes\":[{\"delta\":[-2264,479],\"delevation\":20},{\"delta\":[-580,-14]}],\"laneId\":18,\"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\":19,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916758,39.5953365],[-105.0917363,39.5953365]]},\"properties\":{\"nodes\":[{\"delta\":[-2257,796],\"delevation\":20},{\"delta\":[-518,0]}],\"laneId\":19,\"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\":20,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916734,39.5953755],[-105.0917299,39.5953755]]},\"properties\":{\"nodes\":[{\"delta\":[-2236,1229],\"delevation\":20},{\"delta\":[-484,0]}],\"laneId\":20,\"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\":23,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.091519,39.5954362],[-105.0914927,39.5965421],[-105.091488,39.5974092],[-105.0914843,39.5981324],[-105.0914843,39.5981324],[-105.0914851,39.5981349]]},\"properties\":{\"nodes\":[{\"delta\":[-914,1903],\"delevation\":20},{\"delta\":[225,12288],\"delevation\":30},{\"delta\":[40,9634],\"delevation\":-20},{\"delta\":[32,8035],\"delevation\":-30},{\"delta\":[0,0]},{\"delta\":[-7,28]}],\"laneId\":23,\"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\":7,\"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\":12,\"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\":22,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0915548,39.5954372],[-105.0915441,39.59638],[-105.0915337,39.5974224],[-105.0915281,39.5981345]]},\"properties\":{\"nodes\":[{\"delta\":[-1221,1914],\"delevation\":20},{\"delta\":[92,10475],\"delevation\":30},{\"delta\":[89,11582],\"delevation\":-20},{\"delta\":[48,7912],\"delevation\":-30}],\"laneId\":22,\"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\":7,\"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\":13,\"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\":21,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916048,39.5954391],[-105.0916029,39.5957255],[-105.0916004,39.5962647],[-105.0915885,39.5966976],[-105.0915786,39.5977599],[-105.0915764,39.5981344]]},\"properties\":{\"nodes\":[{\"delta\":[-1649,1935],\"delevation\":20},{\"delta\":[16,3182],\"delevation\":10},{\"delta\":[21,5991],\"delevation\":20},{\"delta\":[102,4810]},{\"delta\":[85,11803],\"delevation\":-30},{\"delta\":[19,4161],\"delevation\":-20}],\"laneId\":21,\"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\":7,\"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\":20,\"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\":24,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0914747,39.5954382],[-105.0914714,39.5958506],[-105.0914611,39.596192],[-105.0914778,39.5963599]]},\"properties\":{\"nodes\":[{\"delta\":[-535,1926],\"delevation\":20},{\"delta\":[28,4582],\"delevation\":10},{\"delta\":[88,3793],\"delevation\":10},{\"delta\":[-143,1866],\"delevation\":10}],\"laneId\":24,\"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\":7,\"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\":6,\"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\":5,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":25,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0914371,39.5954371],[-105.0914355,39.5958506],[-105.0914461,39.596052]]},\"properties\":{\"nodes\":[{\"delta\":[-213,1913],\"delevation\":20},{\"delta\":[14,4594],\"delevation\":10},{\"delta\":[-91,2238],\"delevation\":10}],\"laneId\":25,\"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\":7,\"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\":5,\"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\":5,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":26,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0913276,39.5954361],[-105.0913284,39.5954678]]},\"properties\":{\"nodes\":[{\"delta\":[724,1902],\"delevation\":10},{\"delta\":[-7,352],\"delevation\":10}],\"laneId\":26,\"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\":8,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":27,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0912831,39.5954348],[-105.0912839,39.5954647]]},\"properties\":{\"nodes\":[{\"delta\":[1105,1888],\"delevation\":10},{\"delta\":[-7,332],\"delevation\":10}],\"laneId\":27,\"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\":8,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":28,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0912264,39.5954336],[-105.091228,39.5954647],[-105.091228,39.5954653]]},\"properties\":{\"nodes\":[{\"delta\":[1591,1874],\"delevation\":10},{\"delta\":[-14,346]},{\"delta\":[0,7]}],\"laneId\":28,\"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\":8,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}}]},\"connectingLanesFeatureCollection\":{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"id\":\"2-19\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911523,39.5953376],[-105.0916758,39.5953365]]},\"properties\":{\"signalGroupId\":4,\"ingressLaneId\":2,\"egressLaneId\":19}},{\"type\":\"Feature\",\"id\":\"3-18\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911527,39.5953113],[-105.0916766,39.595308]]},\"properties\":{\"signalGroupId\":4,\"ingressLaneId\":3,\"egressLaneId\":18}},{\"type\":\"Feature\",\"id\":\"1-28\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911535,39.5953745],[-105.0912264,39.5954336]]},\"properties\":{\"ingressLaneId\":1,\"egressLaneId\":28}},{\"type\":\"Feature\",\"id\":\"4-12\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911537,39.5952537],[-105.0915234,39.5950941]]},\"properties\":{\"signalGroupId\":7,\"ingressLaneId\":4,\"egressLaneId\":12}},{\"type\":\"Feature\",\"id\":\"10-26\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0913299,39.5950922],[-105.0913276,39.5954361]]},\"properties\":{\"signalGroupId\":6,\"ingressLaneId\":10,\"egressLaneId\":26}},{\"type\":\"Feature\",\"id\":\"9-27\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0912849,39.5950912],[-105.0912831,39.5954348]]},\"properties\":{\"signalGroupId\":6,\"ingressLaneId\":9,\"egressLaneId\":27}},{\"type\":\"Feature\",\"id\":\"11-18\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0913713,39.5950916],[-105.0916766,39.595308]]},\"properties\":{\"signalGroupId\":1,\"ingressLaneId\":11,\"egressLaneId\":18}},{\"type\":\"Feature\",\"id\":\"8-7\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0912303,39.5950889],[-105.0911443,39.5951332]]},\"properties\":{\"ingressLaneId\":8,\"egressLaneId\":7}},{\"type\":\"Feature\",\"id\":\"15-6\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916931,39.5951733],[-105.0911474,39.5951732]]},\"properties\":{\"signalGroupId\":8,\"ingressLaneId\":15,\"egressLaneId\":6}},{\"type\":\"Feature\",\"id\":\"16-5\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916852,39.5952059],[-105.0911474,39.5952053]]},\"properties\":{\"signalGroupId\":8,\"ingressLaneId\":16,\"egressLaneId\":5}},{\"type\":\"Feature\",\"id\":\"17-26\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916853,39.5952393],[-105.0913276,39.5954361]]},\"properties\":{\"signalGroupId\":3,\"ingressLaneId\":17,\"egressLaneId\":26}},{\"type\":\"Feature\",\"id\":\"14-13\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916926,39.5951407],[-105.0915804,39.5950941]]},\"properties\":{\"ingressLaneId\":14,\"egressLaneId\":13}},{\"type\":\"Feature\",\"id\":\"23-12\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.091519,39.5954362],[-105.0915234,39.5950941]]},\"properties\":{\"signalGroupId\":2,\"ingressLaneId\":23,\"egressLaneId\":12}},{\"type\":\"Feature\",\"id\":\"22-13\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0915548,39.5954372],[-105.0915804,39.5950941]]},\"properties\":{\"signalGroupId\":2,\"ingressLaneId\":22,\"egressLaneId\":13}},{\"type\":\"Feature\",\"id\":\"21-20\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916048,39.5954391],[-105.0916734,39.5953755]]},\"properties\":{\"ingressLaneId\":21,\"egressLaneId\":20}},{\"type\":\"Feature\",\"id\":\"24-6\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0914747,39.5954382],[-105.0911474,39.5951732]]},\"properties\":{\"signalGroupId\":5,\"ingressLaneId\":24,\"egressLaneId\":6}},{\"type\":\"Feature\",\"id\":\"25-5\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0914371,39.5954371],[-105.0911474,39.5952053]]},\"properties\":{\"signalGroupId\":5,\"ingressLaneId\":25,\"egressLaneId\":5}}]},\"properties\":{\"messageType\":\"MAP\",\"odeReceivedAt\":\"2024-05-10T15:57:46.531370277Z\",\"originIp\":\"172.18.0.1\",\"intersectionId\":12110,\"msgIssueRevision\":0,\"revision\":0,\"refPoint\":{\"latitude\":39.5952649,\"longitude\":-105.0914122,\"elevation\":1677.0},\"cti4501Conformant\":false,\"validationMessages\":[{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].id.region: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].id\",\"schemaPath\":\"#/$defs/J2735IntersectionReferenceID/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[2].connectsTo.connectsTo[0].signalGroup: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[2].connectsTo.connectsTo[0]\",\"schemaPath\":\"#/$defs/J2735Connection/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].maneuvers: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].connectsTo: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].maneuvers: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].connectsTo: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].maneuvers: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].connectsTo: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[7].maneuvers: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[7]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[7].connectsTo: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[7]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[8].maneuvers: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[8]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[8].connectsTo: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[8]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[12].connectsTo.connectsTo[0].signalGroup: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[12].connectsTo.connectsTo[0]\",\"schemaPath\":\"#/$defs/J2735Connection/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[16].connectsTo.connectsTo[0].signalGroup: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[16].connectsTo.connectsTo[0]\",\"schemaPath\":\"#/$defs/J2735Connection/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[17].maneuvers: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[17]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[17].connectsTo: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[17]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[18].maneuvers: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[18]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[18].connectsTo: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[18]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[19].maneuvers: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[19]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[19].connectsTo: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[19]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[22].connectsTo.connectsTo[0].signalGroup: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[22].connectsTo.connectsTo[0]\",\"schemaPath\":\"#/$defs/J2735Connection/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[25].maneuvers: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[25]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[25].connectsTo: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[25]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[26].maneuvers: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[26]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[26].connectsTo: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[26]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[27].maneuvers: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[27]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[27].connectsTo: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[27]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].speedLimits: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0]\",\"schemaPath\":\"#/$defs/J2735IntersectionGeometry/required\"}],\"laneWidth\":366,\"mapSource\":\"RSU\",\"timeStamp\":\"2024-05-10T15:57:46.531370277Z\"}}"; + private String processedMapDataReference = "{\"mapFeatureCollection\":{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"id\":2,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911523,39.5953376],[-105.0899239,39.5953231],[-105.0896005,39.5953332],[-105.0888831,39.595317],[-105.088108,39.5953159],[-105.0876637,39.5953153]]},\"properties\":{\"nodes\":[{\"delta\":[2225,808],\"delevation\":-10},{\"delta\":[10517,-161],\"delevation\":-60},{\"delta\":[2769,112],\"delevation\":-10},{\"delta\":[6142,-180],\"delevation\":-30},{\"delta\":[6636,-12],\"delevation\":-20},{\"delta\":[3804,-7]}],\"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\":19,\"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\":4,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":3,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911527,39.5953113],[-105.0908101,39.5953039],[-105.0900791,39.5952962],[-105.0896801,39.5952874],[-105.0888781,39.5952839],[-105.0885678,39.5952789],[-105.0880704,39.5952849],[-105.0876638,39.5952849]]},\"properties\":{\"nodes\":[{\"delta\":[2222,515],\"delevation\":-10},{\"delta\":[2933,-82],\"delevation\":-10},{\"delta\":[6259,-85],\"delevation\":-40},{\"delta\":[3416,-98],\"delevation\":-20},{\"delta\":[6867,-39],\"delevation\":-30},{\"delta\":[2657,-55],\"delevation\":-10},{\"delta\":[4259,67],\"delevation\":-10},{\"delta\":[3481,0]}],\"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\":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\":18,\"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\":4,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":1,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911535,39.5953745],[-105.0899328,39.5953544],[-105.0897165,39.5953447]]},\"properties\":{\"nodes\":[{\"delta\":[2215,1218]},{\"delta\":[10451,-223],\"delevation\":-70},{\"delta\":[1852,-108],\"delevation\":-10}],\"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\":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\":28,\"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\":4,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911537,39.5952537],[-105.0903143,39.5952555],[-105.0902511,39.5952591],[-105.0900995,39.5952797]]},\"properties\":{\"nodes\":[{\"delta\":[2213,-124],\"delevation\":-10},{\"delta\":[7187,20],\"delevation\":-40},{\"delta\":[541,40],\"delevation\":-10},{\"delta\":[1298,229]}],\"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\":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\":12,\"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\":7,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":6,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911474,39.5951732],[-105.0911048,39.5951738]]},\"properties\":{\"nodes\":[{\"delta\":[2267,-1019],\"delevation\":-10},{\"delta\":[365,7]}],\"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.0911474,39.5952053],[-105.0911079,39.5952053]]},\"properties\":{\"nodes\":[{\"delta\":[2267,-662],\"delevation\":-10},{\"delta\":[338,0]}],\"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\":7,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911443,39.5951332],[-105.0911017,39.5951356]]},\"properties\":{\"nodes\":[{\"delta\":[2294,-1463],\"delevation\":-10},{\"delta\":[365,27]}],\"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\":2,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":12,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0915234,39.5950941],[-105.0915234,39.5950602]]},\"properties\":{\"nodes\":[{\"delta\":[-952,-1898]},{\"delta\":[0,-377]}],\"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\":4,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":13,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0915804,39.5950941],[-105.0915796,39.595059],[-105.0915796,39.595059],[-105.0915796,39.5950596]]},\"properties\":{\"nodes\":[{\"delta\":[-1440,-1898]},{\"delta\":[7,-390]},{\"delta\":[0,0]},{\"delta\":[0,7]}],\"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\":4,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":10,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0913299,39.5950922],[-105.09133,39.5944158],[-105.0913348,39.5939413],[-105.0913368,39.592921],[-105.0913348,39.5923866],[-105.0913348,39.5923866],[-105.0913356,39.592386]]},\"properties\":{\"nodes\":[{\"delta\":[705,-1919]},{\"delta\":[-1,-7515],\"delevation\":-40},{\"delta\":[-41,-5272],\"delevation\":10},{\"delta\":[-17,-11337]},{\"delta\":[17,-5938],\"delevation\":10},{\"delta\":[0,0]},{\"delta\":[-7,-7]}],\"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\":0,\"ingressApproach\":3,\"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\":26,\"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\":9,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0912849,39.5950912],[-105.0912838,39.5946624],[-105.0912861,39.5940862],[-105.0912901,39.5933704],[-105.0912882,39.5923858]]},\"properties\":{\"nodes\":[{\"delta\":[1090,-1930],\"delevation\":-10},{\"delta\":[9,-4764],\"delevation\":-10},{\"delta\":[-20,-6402],\"delevation\":-20},{\"delta\":[-34,-7953],\"delevation\":-10},{\"delta\":[16,-10940],\"delevation\":20}],\"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\":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\":27,\"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.0913713,39.5950916],[-105.091375,39.5944888],[-105.0913568,39.5942534]]},\"properties\":{\"nodes\":[{\"delta\":[350,-1925]},{\"delta\":[-32,-6698],\"delevation\":-30},{\"delta\":[156,-2616]}],\"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\":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\":18,\"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\":1,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":8,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0912303,39.5950889],[-105.0912421,39.5949129],[-105.0912427,39.5942475],[-105.0912568,39.5941003]]},\"properties\":{\"nodes\":[{\"delta\":[1557,-1956],\"delevation\":-10},{\"delta\":[-101,-1956]},{\"delta\":[-5,-7393],\"delevation\":-40},{\"delta\":[-121,-1636]}],\"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\":false,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":true,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":7,\"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\":15,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916931,39.5951733],[-105.0927839,39.5951756],[-105.0941732,39.5951722],[-105.0952027,39.5951734]]},\"properties\":{\"nodes\":[{\"delta\":[-2405,-1018],\"delevation\":10},{\"delta\":[-9339,26],\"delevation\":30},{\"delta\":[-11895,-38],\"delevation\":50},{\"delta\":[-8814,13]}],\"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\":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\":6,\"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\":8,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":16,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916852,39.5952059],[-105.0931135,39.5952073],[-105.0944406,39.595204],[-105.0952041,39.5952041]]},\"properties\":{\"nodes\":[{\"delta\":[-2337,-656],\"delevation\":10},{\"delta\":[-12229,15],\"delevation\":50},{\"delta\":[-11362,-37],\"delevation\":30},{\"delta\":[-6537,1]}],\"laneId\":16,\"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\":8,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":17,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916853,39.5952393],[-105.0927239,39.5952377],[-105.0941207,39.5952375],[-105.0943034,39.5952213]]},\"properties\":{\"nodes\":[{\"delta\":[-2338,-284],\"delevation\":10},{\"delta\":[-8892,-18],\"delevation\":30},{\"delta\":[-11959,-2],\"delevation\":50},{\"delta\":[-1564,-180]}],\"laneId\":17,\"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\":true,\"maneuverUTurnAllowed\":false,\"maneuverLeftTurnOnRedAllowed\":false,\"reserved1\":false,\"maneuverRightAllowed\":false,\"maneuverLaneChangeAllowed\":false,\"yieldAllwaysRequired\":false,\"maneuverRightTurnOnRedAllowed\":false,\"caution\":false},\"connectsTo\":[{\"connectingLane\":{\"lane\":26,\"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\":3,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":14,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916926,39.5951407],[-105.0940224,39.5951414],[-105.095203,39.5951408]]},\"properties\":{\"nodes\":[{\"delta\":[-2401,-1380],\"delevation\":10},{\"delta\":[-19947,8],\"delevation\":80},{\"delta\":[-10108,-7]}],\"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\":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\":13,\"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\":18,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916766,39.595308],[-105.0917443,39.5953067]]},\"properties\":{\"nodes\":[{\"delta\":[-2264,479],\"delevation\":20},{\"delta\":[-580,-14]}],\"laneId\":18,\"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\":19,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916758,39.5953365],[-105.0917363,39.5953365]]},\"properties\":{\"nodes\":[{\"delta\":[-2257,796],\"delevation\":20},{\"delta\":[-518,0]}],\"laneId\":19,\"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\":20,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916734,39.5953755],[-105.0917299,39.5953755]]},\"properties\":{\"nodes\":[{\"delta\":[-2236,1229],\"delevation\":20},{\"delta\":[-484,0]}],\"laneId\":20,\"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\":23,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.091519,39.5954362],[-105.0914927,39.5965421],[-105.091488,39.5974092],[-105.0914843,39.5981324],[-105.0914843,39.5981324],[-105.0914851,39.5981349]]},\"properties\":{\"nodes\":[{\"delta\":[-914,1903],\"delevation\":20},{\"delta\":[225,12288],\"delevation\":30},{\"delta\":[40,9634],\"delevation\":-20},{\"delta\":[32,8035],\"delevation\":-30},{\"delta\":[0,0]},{\"delta\":[-7,28]}],\"laneId\":23,\"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\":7,\"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\":12,\"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\":22,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0915548,39.5954372],[-105.0915441,39.59638],[-105.0915337,39.5974224],[-105.0915281,39.5981345]]},\"properties\":{\"nodes\":[{\"delta\":[-1221,1914],\"delevation\":20},{\"delta\":[92,10475],\"delevation\":30},{\"delta\":[89,11582],\"delevation\":-20},{\"delta\":[48,7912],\"delevation\":-30}],\"laneId\":22,\"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\":7,\"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\":13,\"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\":21,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916048,39.5954391],[-105.0916029,39.5957255],[-105.0916004,39.5962647],[-105.0915885,39.5966976],[-105.0915786,39.5977599],[-105.0915764,39.5981344]]},\"properties\":{\"nodes\":[{\"delta\":[-1649,1935],\"delevation\":20},{\"delta\":[16,3182],\"delevation\":10},{\"delta\":[21,5991],\"delevation\":20},{\"delta\":[102,4810]},{\"delta\":[85,11803],\"delevation\":-30},{\"delta\":[19,4161],\"delevation\":-20}],\"laneId\":21,\"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\":7,\"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\":20,\"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\":24,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0914747,39.5954382],[-105.0914714,39.5958506],[-105.0914611,39.596192],[-105.0914778,39.5963599]]},\"properties\":{\"nodes\":[{\"delta\":[-535,1926],\"delevation\":20},{\"delta\":[28,4582],\"delevation\":10},{\"delta\":[88,3793],\"delevation\":10},{\"delta\":[-143,1866],\"delevation\":10}],\"laneId\":24,\"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\":7,\"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\":6,\"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\":5,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":25,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0914371,39.5954371],[-105.0914355,39.5958506],[-105.0914461,39.596052]]},\"properties\":{\"nodes\":[{\"delta\":[-213,1913],\"delevation\":20},{\"delta\":[14,4594],\"delevation\":10},{\"delta\":[-91,2238],\"delevation\":10}],\"laneId\":25,\"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\":7,\"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\":5,\"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\":5,\"connectionID\":1}]}},{\"type\":\"Feature\",\"id\":26,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0913276,39.5954361],[-105.0913284,39.5954678]]},\"properties\":{\"nodes\":[{\"delta\":[724,1902],\"delevation\":10},{\"delta\":[-7,352],\"delevation\":10}],\"laneId\":26,\"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\":8,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":27,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0912831,39.5954348],[-105.0912839,39.5954647]]},\"properties\":{\"nodes\":[{\"delta\":[1105,1888],\"delevation\":10},{\"delta\":[-7,332],\"delevation\":10}],\"laneId\":27,\"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\":8,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}},{\"type\":\"Feature\",\"id\":28,\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0912264,39.5954336],[-105.091228,39.5954647],[-105.091228,39.5954653]]},\"properties\":{\"nodes\":[{\"delta\":[1591,1874],\"delevation\":10},{\"delta\":[-14,346]},{\"delta\":[0,7]}],\"laneId\":28,\"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\":8,\"ingressApproach\":0,\"ingressPath\":false,\"egressPath\":true}}]},\"connectingLanesFeatureCollection\":{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"id\":\"2-19\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911523,39.5953376],[-105.0916758,39.5953365]]},\"properties\":{\"signalGroupId\":4,\"ingressLaneId\":2,\"egressLaneId\":19}},{\"type\":\"Feature\",\"id\":\"3-18\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911527,39.5953113],[-105.0916766,39.595308]]},\"properties\":{\"signalGroupId\":4,\"ingressLaneId\":3,\"egressLaneId\":18}},{\"type\":\"Feature\",\"id\":\"1-28\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911535,39.5953745],[-105.0912264,39.5954336]]},\"properties\":{\"ingressLaneId\":1,\"egressLaneId\":28}},{\"type\":\"Feature\",\"id\":\"4-12\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0911537,39.5952537],[-105.0915234,39.5950941]]},\"properties\":{\"signalGroupId\":7,\"ingressLaneId\":4,\"egressLaneId\":12}},{\"type\":\"Feature\",\"id\":\"10-26\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0913299,39.5950922],[-105.0913276,39.5954361]]},\"properties\":{\"signalGroupId\":6,\"ingressLaneId\":10,\"egressLaneId\":26}},{\"type\":\"Feature\",\"id\":\"9-27\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0912849,39.5950912],[-105.0912831,39.5954348]]},\"properties\":{\"signalGroupId\":6,\"ingressLaneId\":9,\"egressLaneId\":27}},{\"type\":\"Feature\",\"id\":\"11-18\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0913713,39.5950916],[-105.0916766,39.595308]]},\"properties\":{\"signalGroupId\":1,\"ingressLaneId\":11,\"egressLaneId\":18}},{\"type\":\"Feature\",\"id\":\"8-7\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0912303,39.5950889],[-105.0911443,39.5951332]]},\"properties\":{\"ingressLaneId\":8,\"egressLaneId\":7}},{\"type\":\"Feature\",\"id\":\"15-6\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916931,39.5951733],[-105.0911474,39.5951732]]},\"properties\":{\"signalGroupId\":8,\"ingressLaneId\":15,\"egressLaneId\":6}},{\"type\":\"Feature\",\"id\":\"16-5\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916852,39.5952059],[-105.0911474,39.5952053]]},\"properties\":{\"signalGroupId\":8,\"ingressLaneId\":16,\"egressLaneId\":5}},{\"type\":\"Feature\",\"id\":\"17-26\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916853,39.5952393],[-105.0913276,39.5954361]]},\"properties\":{\"signalGroupId\":3,\"ingressLaneId\":17,\"egressLaneId\":26}},{\"type\":\"Feature\",\"id\":\"14-13\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916926,39.5951407],[-105.0915804,39.5950941]]},\"properties\":{\"ingressLaneId\":14,\"egressLaneId\":13}},{\"type\":\"Feature\",\"id\":\"23-12\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.091519,39.5954362],[-105.0915234,39.5950941]]},\"properties\":{\"signalGroupId\":2,\"ingressLaneId\":23,\"egressLaneId\":12}},{\"type\":\"Feature\",\"id\":\"22-13\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0915548,39.5954372],[-105.0915804,39.5950941]]},\"properties\":{\"signalGroupId\":2,\"ingressLaneId\":22,\"egressLaneId\":13}},{\"type\":\"Feature\",\"id\":\"21-20\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0916048,39.5954391],[-105.0916734,39.5953755]]},\"properties\":{\"ingressLaneId\":21,\"egressLaneId\":20}},{\"type\":\"Feature\",\"id\":\"24-6\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0914747,39.5954382],[-105.0911474,39.5951732]]},\"properties\":{\"signalGroupId\":5,\"ingressLaneId\":24,\"egressLaneId\":6}},{\"type\":\"Feature\",\"id\":\"25-5\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-105.0914371,39.5954371],[-105.0911474,39.5952053]]},\"properties\":{\"signalGroupId\":5,\"ingressLaneId\":25,\"egressLaneId\":5}}]},\"properties\":{\"schemaVersion\":-1,\"messageType\":\"MAP\",\"odeReceivedAt\":\"2024-05-10T15:57:46.531370277Z\",\"originIp\":\"172.18.0.1\",\"intersectionId\":12110,\"msgIssueRevision\":0,\"revision\":0,\"refPoint\":{\"latitude\":39.5952649,\"longitude\":-105.0914122,\"elevation\":1677.0},\"cti4501Conformant\":false,\"validationMessages\":[{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].id.region: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].id\",\"schemaPath\":\"#/$defs/J2735IntersectionReferenceID/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[2].connectsTo.connectsTo[0].signalGroup: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[2].connectsTo.connectsTo[0]\",\"schemaPath\":\"#/$defs/J2735Connection/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].maneuvers: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].connectsTo: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].maneuvers: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].connectsTo: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].maneuvers: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].connectsTo: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[7].maneuvers: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[7]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[7].connectsTo: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[7]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[8].maneuvers: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[8]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[8].connectsTo: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[8]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[12].connectsTo.connectsTo[0].signalGroup: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[12].connectsTo.connectsTo[0]\",\"schemaPath\":\"#/$defs/J2735Connection/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[16].connectsTo.connectsTo[0].signalGroup: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[16].connectsTo.connectsTo[0]\",\"schemaPath\":\"#/$defs/J2735Connection/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[17].maneuvers: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[17]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[17].connectsTo: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[17]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[18].maneuvers: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[18]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[18].connectsTo: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[18]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[19].maneuvers: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[19]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[19].connectsTo: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[19]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[22].connectsTo.connectsTo[0].signalGroup: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[22].connectsTo.connectsTo[0]\",\"schemaPath\":\"#/$defs/J2735Connection/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[25].maneuvers: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[25]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[25].connectsTo: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[25]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[26].maneuvers: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[26]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[26].connectsTo: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[26]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[27].maneuvers: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[27]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[27].connectsTo: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[27]\",\"schemaPath\":\"#/$defs/J2735GenericLane/required\"},{\"message\":\"$.payload.data.intersections.intersectionGeometry[0].speedLimits: is missing but it is required\",\"jsonPath\":\"$.payload.data.intersections.intersectionGeometry[0]\",\"schemaPath\":\"#/$defs/J2735IntersectionGeometry/required\"}],\"laneWidth\":366,\"mapSource\":\"RSU\",\"timeStamp\":\"2024-05-10T15:57:46.531370277Z\"}}"; @Test public void testMapGetAsOdeData() { diff --git a/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/decoderTests/SpatDecoderTests.java b/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/decoderTests/SpatDecoderTests.java index 1bc00de3..f439c859 100644 --- a/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/decoderTests/SpatDecoderTests.java +++ b/api/jpo-conflictvisualizer-api/src/test/java/us/dot/its/jpo/ode/api/decoderTests/SpatDecoderTests.java @@ -30,7 +30,7 @@ public class SpatDecoderTests { private String odeSpatDataReference = "{\"metadata\":{\"recordType\":\"spatTx\",\"securityResultCode\":\"success\",\"receivedMessageDetails\":{\"locationData\":{},\"rxSource\":\"NA\"},\"encodings\":[{\"elementName\":\"unsecuredData\",\"elementType\":\"MessageFrame\",\"encodingRule\":\"UPER\"}],\"payloadType\":\"us.dot.its.jpo.ode.model.OdeAsn1Payload\",\"serialId\":{\"streamId\":\"f21c4bce-c04b-4ccb-a854-ca4d2f6da547\",\"bundleSize\":1,\"bundleId\":0,\"recordId\":0,\"serialNumber\":0},\"odeReceivedAt\":\"2024-05-15T19:54:27.056948Z\",\"schemaVersion\":6,\"maxDurationTime\":0,\"sanitized\":false,\"spatSource\":\"V2X\",\"originIp\":\"user-upload\",\"isCertPresent\":false},\"payload\":{\"dataType\":\"us.dot.its.jpo.ode.model.OdeHexByteArray\",\"data\":{\"bytes\":\"001338000817a780000089680500204642b342b34802021a15a955a940181190acd0acd20100868555c555c00104342aae2aae002821a155715570\"}}}"; private String odeSpatDecodedXmlReference = "spatTxsuccessNAunsecuredDataMessageFrameUPERus.dot.its.jpo.ode.model.OdeAsn1Payloadf1438a60-a06a-42b3-a984-56e4d7153a8610002024-05-15T19:44:25.476826092Z60falseV2X172.18.0.1falseMessageFrame19121110000000000000000035176222120221214221812218162212022121821852218521218522185252185221852"; private String odeSpatDecodedDataReference = "{\"metadata\":{\"logFileName\":\"\",\"recordType\":\"spatTx\",\"securityResultCode\":\"success\",\"receivedMessageDetails\":{\"rxSource\":\"NA\"},\"payloadType\":\"us.dot.its.jpo.ode.model.OdeSpatPayload\",\"serialId\":{\"streamId\":\"f1438a60-a06a-42b3-a984-56e4d7153a86\",\"bundleSize\":1,\"bundleId\":0,\"recordId\":0,\"serialNumber\":0},\"odeReceivedAt\":\"2024-05-15T19:44:25.476826092Z\",\"schemaVersion\":6,\"maxDurationTime\":0,\"recordGeneratedAt\":\"\",\"sanitized\":false,\"odePacketID\":\"\",\"odeTimStartDateTime\":\"\",\"spatSource\":\"V2X\",\"originIp\":\"172.18.0.1\",\"isCertPresent\":false},\"payload\":{\"data\":{\"intersectionStateList\":{\"intersectionStatelist\":[{\"id\":{\"id\":12111},\"revision\":0,\"status\":{\"failureFlash\":false,\"noValidSPATisAvailableAtThisTime\":false,\"fixedTimeOperation\":false,\"standbyOperation\":false,\"trafficDependentOperation\":false,\"manualControlIsEnabled\":false,\"off\":false,\"stopTimeIsActivated\":false,\"recentChangeInMAPassignedLanesIDsUsed\":false,\"recentMAPmessageUpdate\":false,\"failureMode\":false,\"noValidMAPisAvailableAtThisTime\":false,\"signalPriorityIsActive\":false,\"preemptIsActive\":false},\"timeStamp\":35176,\"states\":{\"movementList\":[{\"signalGroup\":2,\"state_time_speed\":{\"movementEventList\":[{\"eventState\":\"PROTECTED_MOVEMENT_ALLOWED\",\"timing\":{\"minEndTime\":22120,\"maxEndTime\":22121}}]}},{\"signalGroup\":4,\"state_time_speed\":{\"movementEventList\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":22181,\"maxEndTime\":22181}}]}},{\"signalGroup\":6,\"state_time_speed\":{\"movementEventList\":[{\"eventState\":\"PROTECTED_MOVEMENT_ALLOWED\",\"timing\":{\"minEndTime\":22120,\"maxEndTime\":22121}}]}},{\"signalGroup\":8,\"state_time_speed\":{\"movementEventList\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":21852,\"maxEndTime\":21852}}]}},{\"signalGroup\":1,\"state_time_speed\":{\"movementEventList\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":21852,\"maxEndTime\":21852}}]}},{\"signalGroup\":5,\"state_time_speed\":{\"movementEventList\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":21852,\"maxEndTime\":21852}}]}}]}}]}},\"dataType\":\"us.dot.its.jpo.ode.plugin.j2735.J2735SPAT\"}}"; - private String processedSpatDataReference = "{\"messageType\":\"SPAT\",\"odeReceivedAt\":\"2024-05-15T19:44:25.476826092Z\",\"originIp\":\"172.18.0.1\",\"intersectionId\":12111,\"cti4501Conformant\":false,\"validationMessages\":[{\"message\":\"$.payload.data.timeStamp: is missing but it is required\",\"jsonPath\":\"$.payload.data\",\"schemaPath\":\"#/$defs/J2735SPAT/required\"},{\"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\"}],\"revision\":0,\"status\":{\"manualControlIsEnabled\":false,\"stopTimeIsActivated\":false,\"failureFlash\":false,\"preemptIsActive\":false,\"signalPriorityIsActive\":false,\"fixedTimeOperation\":false,\"trafficDependentOperation\":false,\"standbyOperation\":false,\"failureMode\":false,\"off\":false,\"recentMAPmessageUpdate\":false,\"recentChangeInMAPassignedLanesIDsUsed\":false,\"noValidMAPisAvailableAtThisTime\":false,\"noValidSPATisAvailableAtThisTime\":false},\"utcTimeStamp\":\"2024-05-15T19:44:35.176Z\",\"states\":[{\"signalGroup\":2,\"stateTimeSpeed\":[{\"eventState\":\"PROTECTED_MOVEMENT_ALLOWED\",\"timing\":{\"minEndTime\":\"2024-05-15T19:36:52Z\",\"maxEndTime\":\"2024-05-15T19:36:52.1Z\"}}]},{\"signalGroup\":4,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-05-15T19:36:58.1Z\",\"maxEndTime\":\"2024-05-15T19:36:58.1Z\"}}]},{\"signalGroup\":6,\"stateTimeSpeed\":[{\"eventState\":\"PROTECTED_MOVEMENT_ALLOWED\",\"timing\":{\"minEndTime\":\"2024-05-15T19:36:52Z\",\"maxEndTime\":\"2024-05-15T19:36:52.1Z\"}}]},{\"signalGroup\":8,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-05-15T19:36:25.2Z\",\"maxEndTime\":\"2024-05-15T19:36:25.2Z\"}}]},{\"signalGroup\":1,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-05-15T19:36:25.2Z\",\"maxEndTime\":\"2024-05-15T19:36:25.2Z\"}}]},{\"signalGroup\":5,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-05-15T19:36:25.2Z\",\"maxEndTime\":\"2024-05-15T19:36:25.2Z\"}}]}]}"; + private String processedSpatDataReference = "{\"schemaVersion\":-1,\"messageType\":\"SPAT\",\"odeReceivedAt\":\"2024-05-15T19:44:25.476826092Z\",\"originIp\":\"172.18.0.1\",\"intersectionId\":12111,\"cti4501Conformant\":false,\"validationMessages\":[{\"message\":\"$.payload.data.timeStamp: is missing but it is required\",\"jsonPath\":\"$.payload.data\",\"schemaPath\":\"#/$defs/J2735SPAT/required\"},{\"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\"}],\"revision\":0,\"status\":{\"manualControlIsEnabled\":false,\"stopTimeIsActivated\":false,\"failureFlash\":false,\"preemptIsActive\":false,\"signalPriorityIsActive\":false,\"fixedTimeOperation\":false,\"trafficDependentOperation\":false,\"standbyOperation\":false,\"failureMode\":false,\"off\":false,\"recentMAPmessageUpdate\":false,\"recentChangeInMAPassignedLanesIDsUsed\":false,\"noValidMAPisAvailableAtThisTime\":false,\"noValidSPATisAvailableAtThisTime\":false},\"utcTimeStamp\":\"2024-05-15T19:44:35.176Z\",\"states\":[{\"signalGroup\":2,\"stateTimeSpeed\":[{\"eventState\":\"PROTECTED_MOVEMENT_ALLOWED\",\"timing\":{\"minEndTime\":\"2024-05-15T19:36:52Z\",\"maxEndTime\":\"2024-05-15T19:36:52.1Z\"}}]},{\"signalGroup\":4,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-05-15T19:36:58.1Z\",\"maxEndTime\":\"2024-05-15T19:36:58.1Z\"}}]},{\"signalGroup\":6,\"stateTimeSpeed\":[{\"eventState\":\"PROTECTED_MOVEMENT_ALLOWED\",\"timing\":{\"minEndTime\":\"2024-05-15T19:36:52Z\",\"maxEndTime\":\"2024-05-15T19:36:52.1Z\"}}]},{\"signalGroup\":8,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-05-15T19:36:25.2Z\",\"maxEndTime\":\"2024-05-15T19:36:25.2Z\"}}]},{\"signalGroup\":1,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-05-15T19:36:25.2Z\",\"maxEndTime\":\"2024-05-15T19:36:25.2Z\"}}]},{\"signalGroup\":5,\"stateTimeSpeed\":[{\"eventState\":\"STOP_AND_REMAIN\",\"timing\":{\"minEndTime\":\"2024-05-15T19:36:25.2Z\",\"maxEndTime\":\"2024-05-15T19:36:25.2Z\"}}]}]}"; @Test public void testSpatGetAsOdeData() { From 815ca73ff6b4b20c07c72ec580ea0ed50d37ea47 Mon Sep 17 00:00:00 2001 From: John-Wiens Date: Fri, 31 May 2024 12:53:03 -0600 Subject: [PATCH 29/29] Removed Version label in Conflict Visualizer API --- README.md | 2 +- api/Dockerfile | 4 ++-- api/jpo-conflictvisualizer-api/pom.xml | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 274091d6..1d87585d 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,7 @@ The below section provides additional instruction on how to setup the conflict v The conflict visualizer API requires the following dependencies be installed to run locally -- Java 11 +- Java 21 - Maven Additionally there are other dependencies installed through maven. diff --git a/api/Dockerfile b/api/Dockerfile index 651f1e17..1fbf46a0 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -99,7 +99,7 @@ WORKDIR /home COPY --from=jbuilder /home/jpo-conflictvisualizer-api/src/main/resources/application.yaml /home COPY --from=jbuilder /home/jpo-conflictvisualizer-api/src/main/resources/logback.xml /home -COPY --from=jbuilder /home/jpo-conflictvisualizer-api/target/jpo-conflictvisualizer-api-1.3.0-SNAPSHOT.jar /home +COPY --from=jbuilder /home/jpo-conflictvisualizer-api/target/jpo-conflictvisualizer-api.jar /home # # Copy asn1_codec executable and test files USER root WORKDIR /asn1_codec @@ -141,4 +141,4 @@ ENTRYPOINT ["java", \ "-Dcom.sun.management.jmxremote.ssl=false", \ "-Dlogback.configurationFile=/home/logback.xml", \ "-jar", \ - "/home/jpo-conflictvisualizer-api-1.3.0-SNAPSHOT.jar"] \ No newline at end of file + "/home/jpo-conflictvisualizer-api.jar"] \ No newline at end of file diff --git a/api/jpo-conflictvisualizer-api/pom.xml b/api/jpo-conflictvisualizer-api/pom.xml index 3b9a692c..99849686 100644 --- a/api/jpo-conflictvisualizer-api/pom.xml +++ b/api/jpo-conflictvisualizer-api/pom.xml @@ -12,8 +12,8 @@ usdot.jpo.ode jpo-conflictvisualizer-api 1.3.0-SNAPSHOT + jar jpo-conflictvisualizer-api - jar Conflict Visualizer 21 @@ -223,6 +223,7 @@ + ${project.artifactId} org.jacoco

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