{
    "componentChunkName": "component---src-design-system-templates-category-developers-tsx",
    "path": "/blog/developers/",
    "result": {"data":{"articles":{"edges":[{"node":{"publishDate":"Jul 26, 2022","updatedAt":"Jul 28, 2022","compose__page":[{"title":"Generating Blue Dot Testing Data","slug":"generating-blue-dot-testing-data"}],"tags":{"category":{"name":"Developers","slug":"developers"},"topic":{"name":"Use Cases","slug":"use-cases"}},"content":{"raw":"{\"data\":{},\"content\":[{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Blue Dot is one of the most requested and exciting features of indoor maps and \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://www.mappedin.com/blog/product/positioning/what-is-blue-dot-wayfinding\"},\"content\":[{\"data\":{},\"marks\":[{\"type\":\"underline\"}],\"value\":\"wayfinding\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\". It helps users locate themselves on the map in an instant. With our Mappedin SDK, it only takes a few lines of code to \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/web-sdk/v4/blue-dot\"},\"content\":[{\"data\":{},\"marks\":[{\"type\":\"underline\"}],\"value\":\"enable and draw the Blue Dot on the map\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\".\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Fingerprinting the venue for indoor positioning and quality control of the positioning accuracy require physical presence at the venue. To develop Blue Dot-enabled applications, you’ll need to test them in various ways. Many test cases and prototyping can be done remotely if representative test data is available. While in-person walks to perform testing in a venue has its benefits, it may not be the most practical and using test data provides repeatable and possibly automated test cases for your application.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"At Mappedin, we organize \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://www.mappedin.com/blog/company/culture/employee-q-and-a-ben-liu-coop-student-turned-senior-software-developer/\"},\"content\":[{\"data\":{},\"marks\":[{\"type\":\"underline\"}],\"value\":\"quarterly hackathons\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\" where developers spend a few days to build interesting demos. One of these interesting demos was a project \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://www.mappedin.com/blog/company/culture/employee-q-and-a-ben-liu-coop-student-turned-senior-software-developer/\"},\"content\":[{\"data\":{},\"marks\":[{\"type\":\"underline\"}],\"value\":\"Ben Liu\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\" (Staff Software Developer, Team Lead) and I created for generating fake Blue Dot traces for testing purposes. \",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"During the latest Hackedin, I moved it to a CodeSandbox, so that anyone can take advantage of easy test data generation. The source code and the application are available at \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://codesandbox.io/s/bluedot-data-faker-wilhwv\"},\"content\":[{\"data\":{},\"marks\":[{\"type\":\"underline\"}],\"value\":\"https://codesandbox.io/s/bluedot-data-faker-wilhwv\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\".\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"<iframe src=\\\"https://codesandbox.io/embed/bluedot-data-faker-wilhwv?fontsize=14&hidenavigation=1&theme=light&view=preview\\\" style=\\\"width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;\\\" title=\\\"bluedot-data-faker\\\" allow=\\\"geolocation;\\\"></iframe>\\n\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"To open the application in full page view, visit \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://wilhwv.csb.app\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"https://wilhwv.csb.app\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\".\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"To start generating data, click on locations and set them as start and destination positions. Click \",\"nodeType\":\"text\"},{\"data\":{},\"marks\":[{\"type\":\"bold\"}],\"value\":\"Generate \",\"nodeType\":\"text\"},{\"data\":{},\"marks\":[],\"value\":\"to see a route calculated using the paths in Mappedin’s data and the Blue Dot walking along that path. To generate some noise in the data, it’s possible to have the Blue Dot randomly miss the exact path by increasing the jitter value. You can also adjust the accuracy value, which will display the transparent inaccuracy indicator underneath the Blue Dot.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"The output is a JSON array of geolocations, following the style of \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mozilla.org/en-US/docs/Web/API/GeolocationPosition\"},\"content\":[{\"data\":{},\"marks\":[{\"type\":\"underline\"}],\"value\":\"GeolocationPosition\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\" Web API.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{\"target\":{\"sys\":{\"id\":\"5ekBTFEQtFc9R8EoxC1aIA\",\"type\":\"Link\",\"linkType\":\"Entry\"}}},\"content\":[],\"nodeType\":\"embedded-entry-block\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"While this example shows our Mappedin Demo Mall, you can easily change the venue slug, client id, and client secret values to generate data for your own venue. You can download the generated positions in JSON format. To test the data in your application, you could use a similar Blue Dot \",\"nodeType\":\"text\"},{\"data\":{},\"marks\":[{\"type\":\"bold\"}],\"value\":\"PositionUpdater\",\"nodeType\":\"text\"},{\"data\":{},\"marks\":[],\"value\":\" to what’s demonstrated by the data generator to play back the “recording”.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"In a follow-up blog post, I’ll dive more into a CodeSandbox for visualizing positions, including some additional samples for our Mappedin Web App as well as mobile apps.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"}],\"nodeType\":\"document\"}"},"articleType":"Dev Content","image":{"fluid":{"base64":"data:image/png;base64,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","aspectRatio":1.8014492753623188,"src":"//images.ctfassets.net/wdjnw2prxlw8/4f6w7xYxTgGdZe03C5Fo72/1c94f15d052d0eaeb527028221626138/Screenshot_2022-07-21_at_15.20.56.png?w=480&q=90","srcSet":"//images.ctfassets.net/wdjnw2prxlw8/4f6w7xYxTgGdZe03C5Fo72/1c94f15d052d0eaeb527028221626138/Screenshot_2022-07-21_at_15.20.56.png?w=120&h=67&q=90 120w,\n//images.ctfassets.net/wdjnw2prxlw8/4f6w7xYxTgGdZe03C5Fo72/1c94f15d052d0eaeb527028221626138/Screenshot_2022-07-21_at_15.20.56.png?w=240&h=133&q=90 240w,\n//images.ctfassets.net/wdjnw2prxlw8/4f6w7xYxTgGdZe03C5Fo72/1c94f15d052d0eaeb527028221626138/Screenshot_2022-07-21_at_15.20.56.png?w=480&h=266&q=90 480w,\n//images.ctfassets.net/wdjnw2prxlw8/4f6w7xYxTgGdZe03C5Fo72/1c94f15d052d0eaeb527028221626138/Screenshot_2022-07-21_at_15.20.56.png?w=720&h=400&q=90 720w,\n//images.ctfassets.net/wdjnw2prxlw8/4f6w7xYxTgGdZe03C5Fo72/1c94f15d052d0eaeb527028221626138/Screenshot_2022-07-21_at_15.20.56.png?w=960&h=533&q=90 960w,\n//images.ctfassets.net/wdjnw2prxlw8/4f6w7xYxTgGdZe03C5Fo72/1c94f15d052d0eaeb527028221626138/Screenshot_2022-07-21_at_15.20.56.png?w=1243&h=690&q=90 1243w","srcWebp":"//images.ctfassets.net/wdjnw2prxlw8/4f6w7xYxTgGdZe03C5Fo72/1c94f15d052d0eaeb527028221626138/Screenshot_2022-07-21_at_15.20.56.png?w=480&q=90&fm=webp","srcSetWebp":"//images.ctfassets.net/wdjnw2prxlw8/4f6w7xYxTgGdZe03C5Fo72/1c94f15d052d0eaeb527028221626138/Screenshot_2022-07-21_at_15.20.56.png?w=120&h=67&q=90&fm=webp 120w,\n//images.ctfassets.net/wdjnw2prxlw8/4f6w7xYxTgGdZe03C5Fo72/1c94f15d052d0eaeb527028221626138/Screenshot_2022-07-21_at_15.20.56.png?w=240&h=133&q=90&fm=webp 240w,\n//images.ctfassets.net/wdjnw2prxlw8/4f6w7xYxTgGdZe03C5Fo72/1c94f15d052d0eaeb527028221626138/Screenshot_2022-07-21_at_15.20.56.png?w=480&h=266&q=90&fm=webp 480w,\n//images.ctfassets.net/wdjnw2prxlw8/4f6w7xYxTgGdZe03C5Fo72/1c94f15d052d0eaeb527028221626138/Screenshot_2022-07-21_at_15.20.56.png?w=720&h=400&q=90&fm=webp 720w,\n//images.ctfassets.net/wdjnw2prxlw8/4f6w7xYxTgGdZe03C5Fo72/1c94f15d052d0eaeb527028221626138/Screenshot_2022-07-21_at_15.20.56.png?w=960&h=533&q=90&fm=webp 960w,\n//images.ctfassets.net/wdjnw2prxlw8/4f6w7xYxTgGdZe03C5Fo72/1c94f15d052d0eaeb527028221626138/Screenshot_2022-07-21_at_15.20.56.png?w=1243&h=690&q=90&fm=webp 1243w","sizes":"(max-width: 480px) 100vw, 480px"}}}},{"node":{"publishDate":"Jul 22, 2022","updatedAt":"Jul 22, 2022","compose__page":[{"title":"Mappedin Developer Portal Launch","slug":"mappedin-developer-portal-launch"}],"tags":{"category":{"name":"Developers","slug":"developers"},"topic":{"name":"Features","slug":"features"}},"content":{"raw":"{\"data\":{},\"content\":[{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Something we focus on at Mappedin is building a great experience for developers who use our products. More often than not, we encounter questions, suggestions, and improvements that we take into consideration when building our products. This feedback loop is important as we take steps to refine how developers interact with our SDKs to build their solutions. \",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"We recently made a major step in evolving Mappedin’s Developer Portal, where you can find the guides, code samples, and reference docs you need to build a completely custom mapping solution on your website or app with Mappedin SDKs. We’ve made many improvements to our Developer Portal, and this blog will cover what’s new, what’s included, and how to test our SDK features and capabilities in real-time!\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{\"target\":{\"sys\":{\"id\":\"3PKR0XcFQy9HBjO4OvcdS4\",\"type\":\"Link\",\"linkType\":\"Asset\"}}},\"content\":[],\"nodeType\":\"embedded-asset-block\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"What is the Developer Portal?\",\"nodeType\":\"text\"}],\"nodeType\":\"heading-1\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"\",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Mappedin’s Developer Portal\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\" provides well-documented resources for developers to create their own unique mapping experiences. Documentation on the features and components across all of our \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Mobile and Web SDK products\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\" can be found, along with an up-to-date version release roadmap, integration guides, and a new Developer Playground. \",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"What’s New?\",\"nodeType\":\"text\"}],\"nodeType\":\"heading-1\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"We have expanded the range and depth of documentation to support indoor mapping capabilities for mobile and web-based applications across a variety of use cases. There are more interactive examples and an exciting new addition — the \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/playground/latest/\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Developer Playground\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\"! Developers can now test our Web SDK in real-time to explore its endless capabilities.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"We’ve also made it easier to find documentation support for the latest and earlier SDK versions, along with release notes and migration guides that were previously hosted on the Developer Blog. \",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Developer Portal Guide\",\"nodeType\":\"text\"}],\"nodeType\":\"heading-1\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"What does the Developer Portal include? Let’s delve in. \",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"SDK Documentation \",\"nodeType\":\"text\"}],\"nodeType\":\"heading-2\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"We offer well-documented resources for all features and components across \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/web-sdk/latest/getting-started\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Web\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\", \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/react-native-sdk/latest/getting-started\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"React Native\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\", \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/ios-sdk/latest/getting-started\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"iOS\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\", and \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/android-sdk/latest/getting-started\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Android\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\". This includes a getting started guide, API reference, sample application, release notes, and migration guides. Mappedin API trial keys are also available to provide access to sample data.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Getting Started Guide\",\"nodeType\":\"text\"}],\"nodeType\":\"heading-3\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"With our \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/web-sdk/latest/getting-started\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"getting started guides\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\" for each SDK, developers can get up and running quickly and experience the customization our products have to offer. Simply copy and paste code that will load the Mappedin SDK in its respective platform, display the map, and play around with features and capabilities.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"API Reference\",\"nodeType\":\"text\"}],\"nodeType\":\"heading-3\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Developers can refer to our \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/web-sdk-api/latest/\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"API Reference\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\", which lists the classes and methods of what our code can do. \",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Guides\",\"nodeType\":\"text\"}],\"nodeType\":\"heading-3\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Our SDK documentation includes step-by-step guides on how to add features to the map. We walk users through concise steps and provide sample code for developers to copy and paste. We have guides on our most used features for \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/web-sdk/latest/getting-started\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Web\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\", \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/react-native-sdk/latest/getting-started\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"React Native\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\", \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/ios-sdk/latest/getting-started\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"iOS\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\", and \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/android-sdk/latest/getting-started\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Android\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\".\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Release Notes & Migration Guides\",\"nodeType\":\"text\"}],\"nodeType\":\"heading-3\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"We provide documentation for release upgrades and changes to our SDKs. For major changes, you can refer to our \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/web-sdk/latest/migration-guide\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"migration guides\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\", and minor changes can be found in our \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/web-sdk/latest/release-notes\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"release notes\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\". \",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Developer Playground\",\"nodeType\":\"text\"}],\"nodeType\":\"heading-2\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"An exciting new addition to the Developer Portal is the \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/playground/latest/\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Developer Playground\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\" — an interactive space that enables developers to modify the existing code and visualize our SDK features in real-time, including:\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"content\":[{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"\",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/playground/v4/adding-interactivity\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Adding Interactivity\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\"\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"}],\"nodeType\":\"list-item\"},{\"data\":{},\"content\":[{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"\",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/playground/v4/markers\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Markers\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\"\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"}],\"nodeType\":\"list-item\"},{\"data\":{},\"content\":[{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"\",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/playground/v4/flat-labels\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Flat Labels\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\"\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"}],\"nodeType\":\"list-item\"},{\"data\":{},\"content\":[{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"\",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/playground/v4/floating-labels\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Floating Labels\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\"\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"}],\"nodeType\":\"list-item\"},{\"data\":{},\"content\":[{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"\",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/playground/v4/a-b-wayfinding\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"A-B Wayfinding\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\"\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"}],\"nodeType\":\"list-item\"},{\"data\":{},\"content\":[{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"\",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/playground/v4/camera-controls\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Camera Controls\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\"\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"}],\"nodeType\":\"list-item\"}],\"nodeType\":\"unordered-list\"},{\"data\":{\"target\":{\"sys\":{\"id\":\"5RzmVd8MWRClEWAHoNKMcc\",\"type\":\"Link\",\"linkType\":\"Asset\"}}},\"content\":[],\"nodeType\":\"embedded-asset-block\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Customize maps, test features and functionality, and explore capabilities. There is also the ability to edit and experiment with more options in a public CodeSandbox. We will be adding more tutorials soon so that everyone can use the Developer Playground with ease. \",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Release Roadmap\",\"nodeType\":\"text\"}],\"nodeType\":\"heading-2\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Our release roadmap for the entire Mappedin SDK suite across different platforms is standardized. The \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/releases/\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"release roadmap\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\" showcases the timeline of how each of our SDKs go into different major versions, “Unstable”, “Current”, “Long Term Support” and “Deprecated” so that Mappedin customers can upgrade based on their unique requirements.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"content\":[{\"data\":{},\"content\":[{\"data\":{},\"marks\":[{\"type\":\"bold\"}],\"value\":\"Unstable:\",\"nodeType\":\"text\"},{\"data\":{},\"marks\":[],\"value\":\" Mappedin is actively developing new features that will be included in our beta release. \",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"}],\"nodeType\":\"list-item\"},{\"data\":{},\"content\":[{\"data\":{},\"content\":[{\"data\":{},\"marks\":[{\"type\":\"bold\"}],\"value\":\"Current:\",\"nodeType\":\"text\"},{\"data\":{},\"marks\":[],\"value\":\" The release is actively being developed and customers are treated as stakeholders for adding requirements and adjustments to the product roadmap.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"}],\"nodeType\":\"list-item\"},{\"data\":{},\"content\":[{\"data\":{},\"content\":[{\"data\":{},\"marks\":[{\"type\":\"bold\"}],\"value\":\"Long Term Support:\",\"nodeType\":\"text\"},{\"data\":{},\"marks\":[],\"value\":\" There is no active product roadmap, meaning there will be no new features added; however, Mappedin may from time-to-time choose to fix API level bugs.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"}],\"nodeType\":\"list-item\"},{\"data\":{},\"content\":[{\"data\":{},\"content\":[{\"data\":{},\"marks\":[{\"type\":\"bold\"}],\"value\":\"Deprecate:\",\"nodeType\":\"text\"},{\"data\":{},\"marks\":[],\"value\":\" There will be no new features added, and bugs will not be resolved. \",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"}],\"nodeType\":\"list-item\"}],\"nodeType\":\"unordered-list\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Integrations\",\"nodeType\":\"text\"}],\"nodeType\":\"heading-2\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Documentation of possible mapping integrations is also available in the Developer Portal. This includes \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/data-sync/overview\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Data Sync\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\", \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/geojson-export/overview\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"GeoJSON Export\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\", and \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/pre-built-applications/responsive-web-app-guide\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Pre-Built Applications\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\". These documents outline hardware and software requirements, how to format data, and how to integrate these products into a map. We also provide code snippets for developers to easily read the functionality.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"New Developer Blog\",\"nodeType\":\"text\"}],\"nodeType\":\"heading-2\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"With the launch of the new Developer Portal, we have merged the \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://www.mappedin.com/blog/developers\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"developer blog\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\" with our \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://www.mappedin.com/blog\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"main blog\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\" so that all content is discoverable from one central location. However, users can search for developer content on the Developer Portal for quick and easy access. Developer content is focused on industry trends and unique use cases such as \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://www.mappedin.com/blog/developers/use-cases/augmenting-directions/\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Augmenting Directions With Wait Time Information\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\".\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{\"target\":{\"sys\":{\"id\":\"jkD16NKgOwV1LuqHgzihV\",\"type\":\"Link\",\"linkType\":\"Asset\"}}},\"content\":[],\"nodeType\":\"embedded-asset-block\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Github\",\"nodeType\":\"text\"}],\"nodeType\":\"heading-2\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"We also have quick access to Github on the Developer Portal. You can find additional examples and sample applications \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://github.com/MappedIn\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"here\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\". \",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Start Using SDKs\",\"nodeType\":\"text\"}],\"nodeType\":\"heading-1\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"With access to getting started guides, sample applications, and our professional developer network, we can help bring your mapping vision to life. Explore our SDK features and capabilities in real-time in the \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Developer Playground\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\", and \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://www.mappedin.com/contact-us/\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"contact us today\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\" to start creating your own custom mapping experience.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"}],\"nodeType\":\"document\"}"},"articleType":"Article","image":{"fluid":{"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAMAAADqmnyMAAABP2lDQ1BpY2MAAHjaY2BgEkksKMhhYWBgyM0rKQpyd1KIiIxSYH/GwM4gwiDAwMkgkphcXOAYEOADVMIAo1HBt2sMjCD6si7IrIqZ3DkR9aYfwz13z1is1f+JAT/gSkktTgbSf4A4I7mgqISBgTEFyFYuLykAsTuAbJEioKOA7DkgdjqEvQHEToKwj4DVhAQ5A9k3gGyB5IxEoBmML4BsnSQk8XQkNtReEOBzcfXxUQh2NTIxMvKIYKAuKEmtKAHRzvkFlUWZ6RklCo7AUEpV8MxL1tNRMDIwMmJgAIU5RPXnIHBYMortQ4jlL2FgsPjGwMA8ESGWNIWBYXsbA4PELYSYyjwGBv4WBoZthwoSixLhDmD8xlKcZmwEYfPYMzCw3v3//7MGAwP7RAaGvxP///+9+P//v4uB5t9mYDhQCQDw/mEDW4tHqwAAALdQTFRFHRweIR8hIB4gHh0eIiEiHhweHRwdIiAiHx0fISAiGhkaHRsdJCIkHBocHx4fIyEjJSMlJyUnGhgaIB8gISAhJiQmHBscGxkbIB8hJCMkJSQlJyYnIiEjZ0dIJiUmKyAgoGBdJyYoKSQkKCcoRycjKiQlLSstOCUjRyMeRCUgSysnNU1vHBsdJCAiSSgjKCYnSCsmSCUfMiEfKigqJiUnPyEdRiMdKCAhKCYoIyIjLCosKSgpKikqsL4JKwAAAAlwSFlzAAAXEQAAFxEByibzPwAAAAd0SU1FB+YJEw4wMl56gkYAAAD0SURBVBgZLcHZQsJADAXQJCWdaDpkuGUo4i7u4ooLiv//XfrAOUQszYi1VdEmmewx8z65qbALd6xZhcdRJiDvpzJuvVSfeYpu4AxQnqdJcBxoaoTr4AUAjRbC5VANRxF9LYUyQAUWsTiupmrSM7cOkJxMVU/Pzh0zucizARkgj1KWl1fXOr+5vbt/CFkBhDgo9vj0/PL6tqjrsFkGCB7vNX18fh1aKWlt1g8gDCKtd8kz5+K80bFlAlo1SamE9833YlndlIDs/7SfTEJN4qeqE3Zc6rpz63U7BmFnNfDmN7RhAwg7ZCnXrallgLDjbZh0rAOAP5nmFVW6ArXWAAAAEnRFWHRleGlmOkV4aWZPZmZzZXQAMjZTG6JlAAAAGHRFWHRleGlmOlBpeGVsWERpbWVuc2lvbgA2MDk6oe5MAAAAGHRFWHRleGlmOlBpeGVsWURpbWVuc2lvbgA1MDNFPVh9AAAAXHRFWHRleGlmOlVzZXJDb21tZW50ADY1LCA4MywgNjcsIDczLCA3MywgMCwgMCwgMCwgODMsIDk5LCAxMTQsIDEwMSwgMTAxLCAxMTAsIDExNSwgMTA0LCAxMTEsIDExNkC4H3IAAAAodEVYdGljYzpjb3B5cmlnaHQAQ29weXJpZ2h0IEFwcGxlIEluYy4sIDIwMjLktL+cAAAAHXRFWHRpY2M6ZGVzY3JpcHRpb24AREVMTCBTRTI0MjJIWLnpeG8AAAAASUVORK5CYII=","aspectRatio":1.2107355864811133,"src":"//images.ctfassets.net/wdjnw2prxlw8/6SsBxZ7599ru4Iu77CxzSB/2ef6e298d62df94e8ca044b6a5b83c42/Screen_Shot_2022-07-20_at_4.50.24_PM.png?w=480&q=90","srcSet":"//images.ctfassets.net/wdjnw2prxlw8/6SsBxZ7599ru4Iu77CxzSB/2ef6e298d62df94e8ca044b6a5b83c42/Screen_Shot_2022-07-20_at_4.50.24_PM.png?w=120&h=99&q=90 120w,\n//images.ctfassets.net/wdjnw2prxlw8/6SsBxZ7599ru4Iu77CxzSB/2ef6e298d62df94e8ca044b6a5b83c42/Screen_Shot_2022-07-20_at_4.50.24_PM.png?w=240&h=198&q=90 240w,\n//images.ctfassets.net/wdjnw2prxlw8/6SsBxZ7599ru4Iu77CxzSB/2ef6e298d62df94e8ca044b6a5b83c42/Screen_Shot_2022-07-20_at_4.50.24_PM.png?w=480&h=396&q=90 480w,\n//images.ctfassets.net/wdjnw2prxlw8/6SsBxZ7599ru4Iu77CxzSB/2ef6e298d62df94e8ca044b6a5b83c42/Screen_Shot_2022-07-20_at_4.50.24_PM.png?w=609&h=503&q=90 609w","srcWebp":"//images.ctfassets.net/wdjnw2prxlw8/6SsBxZ7599ru4Iu77CxzSB/2ef6e298d62df94e8ca044b6a5b83c42/Screen_Shot_2022-07-20_at_4.50.24_PM.png?w=480&q=90&fm=webp","srcSetWebp":"//images.ctfassets.net/wdjnw2prxlw8/6SsBxZ7599ru4Iu77CxzSB/2ef6e298d62df94e8ca044b6a5b83c42/Screen_Shot_2022-07-20_at_4.50.24_PM.png?w=120&h=99&q=90&fm=webp 120w,\n//images.ctfassets.net/wdjnw2prxlw8/6SsBxZ7599ru4Iu77CxzSB/2ef6e298d62df94e8ca044b6a5b83c42/Screen_Shot_2022-07-20_at_4.50.24_PM.png?w=240&h=198&q=90&fm=webp 240w,\n//images.ctfassets.net/wdjnw2prxlw8/6SsBxZ7599ru4Iu77CxzSB/2ef6e298d62df94e8ca044b6a5b83c42/Screen_Shot_2022-07-20_at_4.50.24_PM.png?w=480&h=396&q=90&fm=webp 480w,\n//images.ctfassets.net/wdjnw2prxlw8/6SsBxZ7599ru4Iu77CxzSB/2ef6e298d62df94e8ca044b6a5b83c42/Screen_Shot_2022-07-20_at_4.50.24_PM.png?w=609&h=503&q=90&fm=webp 609w","sizes":"(max-width: 480px) 100vw, 480px"}}}},{"node":{"publishDate":"Mar 23, 2022","updatedAt":"Jul 26, 2022","compose__page":[{"title":"Augmenting Directions with Wait Time Information","slug":"augmenting-directions"}],"tags":{"category":{"name":"Developers","slug":"developers"},"topic":{"name":"Use Cases","slug":"use-cases"}},"content":{"raw":"{\"nodeType\":\"document\",\"data\":{},\"content\":[{\"nodeType\":\"embedded-asset-block\",\"data\":{\"target\":{\"sys\":{\"id\":\"jkD16NKgOwV1LuqHgzihV\",\"type\":\"Link\",\"linkType\":\"Asset\"}}},\"content\":[]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Navigating an airport is one of the most stressful parts of traveling. To avoid the hassle, airports can provide \",\"marks\":[],\"data\":{}},{\"nodeType\":\"hyperlink\",\"data\":{\"uri\":\"https://www.mappedin.com/blog/use-cases/transportation/indoor-mapping-in-airports-enhancing-the-passenger-experience\"},\"content\":[{\"nodeType\":\"text\",\"value\":\"detailed digital maps\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"text\",\"value\":\" including a \",\"marks\":[],\"data\":{}},{\"nodeType\":\"hyperlink\",\"data\":{\"uri\":\"https://www.mappedin.com/blog/use-cases/transportation/blue-dot-navigation/airports\"},\"content\":[{\"nodeType\":\"text\",\"value\":\"Blue Dot wayfinding\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"text\",\"value\":\" feature using the Mappedin SDKs. With the flexibility and the power of the Mappedin SDK, it's possible to overlay more detailed information such as walking times and real-time wait time estimates at checkpoints.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"The interactive demo below shows a simple implementation of this concept. You can also open the Sandbox in its own window or drag from the left sidebar to see and modify the sample code. To use the demo below:\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"ordered-list\",\"data\":{},\"content\":[{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Click on a location near the entrance (not a gate)\",\"marks\":[],\"data\":{}}]}]},{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Click on a plane to navigate to that gate\",\"marks\":[],\"data\":{}}]}]},{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"To reset the map, click outside of interactive location polygons\",\"marks\":[],\"data\":{}}]}]}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"<iframe src=\\\"https://codesandbox.io/embed/mappedin-airport-checkpoint-zoobe?fontsize=14&hidenavigation=1&theme=dark&view=preview\\\"\\n     style=\\\"width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;\\\"\\n     title=\\\"mappedin-airport-checkpoint\\\"\\n     allow=\\\"accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking\\\"\\n     sandbox=\\\"allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts\\\"\\n   ></iframe>\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"After you have selected start and destination locations on the map, the path is calculated with \",\"marks\":[],\"data\":{}},{\"nodeType\":\"text\",\"value\":\"directionsTo(destination)\",\"marks\":[{\"type\":\"code\"},{\"type\":\"bold\"}],\"data\":{}},{\"nodeType\":\"text\",\"value\":\" that returns the path, distance and instructions:\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"embedded-entry-block\",\"data\":{\"target\":{\"sys\":{\"id\":\"pSmr7NVVnNE720Bpt8yJx\",\"type\":\"Link\",\"linkType\":\"Entry\"}}},\"content\":[]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"The \",\"marks\":[],\"data\":{}},{\"nodeType\":\"text\",\"value\":\"directions\",\"marks\":[{\"type\":\"code\"},{\"type\":\"bold\"}],\"data\":{}},{\"nodeType\":\"text\",\"value\":\" object can be used for more than just drawing the path on the map. The overall distance returned will help us calculate walking time to the destination. The \",\"marks\":[],\"data\":{}},{\"nodeType\":\"text\",\"value\":\"path\",\"marks\":[{\"type\":\"code\"},{\"type\":\"bold\"}],\"data\":{}},{\"nodeType\":\"text\",\"value\":\" will be used to go over the nodes in the navigation graph.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"embedded-entry-block\",\"data\":{\"target\":{\"sys\":{\"id\":\"2n2K0lLxObFNttkrmMahm7\",\"type\":\"Link\",\"linkType\":\"Entry\"}}},\"content\":[]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"We iterate over the directions to display wait times on the map at every checkpoint node. Every checkpoint gets a marker, which is formatted based on the delay caused and the wait time.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"In this example the wait times are randomized in the \",\"marks\":[],\"data\":{}},{\"nodeType\":\"text\",\"value\":\"Checkpoints\",\"marks\":[{\"type\":\"code\"},{\"type\":\"bold\"}],\"data\":{}},{\"nodeType\":\"text\",\"value\":\" class and retrieved with \",\"marks\":[],\"data\":{}},{\"nodeType\":\"text\",\"value\":\"checkpoints.getCheckpointWaitTime(pathNode)\",\"marks\":[{\"type\":\"code\"},{\"type\":\"bold\"}],\"data\":{}},{\"nodeType\":\"text\",\"value\":\". However, this data could be easily fetched from an API.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"rank: COLLISION_RANKING_TIERS.ALWAYS_VISIBLE\",\"marks\":[{\"type\":\"code\"},{\"type\":\"bold\"}],\"data\":{}},{\"nodeType\":\"text\",\"value\":\" ensures that the marker is visible\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"embedded-entry-block\",\"data\":{\"target\":{\"sys\":{\"id\":\"5PMhO26q4h5SjSpCuz1ilp\",\"type\":\"Link\",\"linkType\":\"Entry\"}}},\"content\":[]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Once we have drawn the markers on the map, we draw the route and set a custom path color within the \",\"marks\":[],\"data\":{}},{\"nodeType\":\"hyperlink\",\"data\":{\"uri\":\"https://developer.mappedin.com/docs/web/latest/modules.html#TPathOptions\"},\"content\":[{\"nodeType\":\"text\",\"value\":\"pathOptions\",\"marks\":[{\"type\":\"code\"},{\"type\":\"bold\"}],\"data\":{}}]},{\"nodeType\":\"text\",\"value\":\".\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"embedded-entry-block\",\"data\":{\"target\":{\"sys\":{\"id\":\"5NZrSbm5DBGOvhabGcjXOZ\",\"type\":\"Link\",\"linkType\":\"Entry\"}}},\"content\":[]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"This shows just one way in which the directions from the Mappedin SDK can be augmented. An airport map could also include a marker to display gate information or some paths could be temporarily restricted based on real-world conditions such as swing gates.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Combining your dynamic data with \",\"marks\":[],\"data\":{}},{\"nodeType\":\"hyperlink\",\"data\":{\"uri\":\"https://www.mappedin.com/blog/product/map-editor/product-101-map-editor/\"},\"content\":[{\"nodeType\":\"text\",\"value\":\"Mappedin's easily managed indoor maps\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"text\",\"value\":\" creates exciting opportunities.\",\"marks\":[],\"data\":{}}]}]}"},"articleType":"Dev Content","image":{"fluid":{"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAMAAACDi47UAAABN2lDQ1BpY2MAAHjaY2BgEkgsKMhhYWBgyM0rKQpyd1KIiIxSYH/GwM7Ax8DBYMGgnJhcXOAYEOADVMIAo1HBt2sMjCD6si7IrC/uRsKxeRpm+0/HOyxm+vCTAT/gSkktTgbSf4A4ObmgqISBgTEByFYuLykAsVuAbJEioKOA7BkgdjqEvQbEToKwD4DVhAQ5A9lXgGyB5IzEFCD7CZCtk4Qkno7EhtoLApxGJu5G4YbuJgxUBiWpFSUg2jm/oLIoMz2jRMERGEKpCp55yXo6CkYGRkYMDKDwhqj+fAMcjoxiHAixGmUGBstaBgbmVIRY0HYGhp1iDAy8xQgxdWD48eYwMBwKKUgsSoQ7gPEbS3GasRGEzQ3Uxzrt///P4QwM7JoMDH+v////e/v//3+XAc2/xcBw4BsAaptd2v6ubNIAAAD/UExURf///+De1f39/djd6fr6+tTU1Pb29vvz8+m0tuKdn+q8vcbGxszMzP7+/t7i6/n5+fHx8dHW0ufn5//+/v77/PDw8e3h4/j4+NbW19TV1vv59dTZzerp5fbz7/nx8fz8/OPk5dLT1Ozs7NDR0+Tk5O/v79fX1sXFxdjW1L7DvMrSwtXXzcvP1d/f3/Dw8MzP09XY2/v7++3t7bi4t6eoq7W1t8DBxLWzs7a7s6WxqcjHx8nKzsbHyM7P0NbW1sDEy+Li4srKysK+vsbEyNXW3Ovt8u3v8vDy9d/e4s3M0Lu/xqusrs3Nzff39+Xl5efo7Pz8/eLj5728wcnJyuns8DCeYYgAAAAJcEhZcwAAFxEAABcRAcom8z8AAAAHdElNRQfmCQ0SBRiCCvOeAAAAdElEQVQIHX3BTQ7BYBSG0ffhphG039BM7aBhJxILlVhIN2BuQBjcQUX44oq/xMA5+ouePlDo32S8uWSqyJKPCQeFVGAT2F+lIbCbnlw1YIPzaMZTwjolguZ8tQugddO2qxpemhLI2eQ6lrDWwxIuG/1aFQp3XecY4BqsBqgAAAASdEVYdGV4aWY6RXhpZk9mZnNldAAyNlMbomUAAAAZdEVYdGV4aWY6UGl4ZWxYRGltZW5zaW9uADI2MzT95rQCAAAAGXRFWHRleGlmOlBpeGVsWURpbWVuc2lvbgAxMzI0SXgzrgAAACh0RVh0aWNjOmNvcHlyaWdodABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAyMuS0v5wAAAAYdEVYdGljYzpkZXNjcmlwdGlvbgAyNEcyVzFHNKmtL4wAAAAASUVORK5CYII=","aspectRatio":1.9894259818731117,"src":"//images.ctfassets.net/wdjnw2prxlw8/jkD16NKgOwV1LuqHgzihV/c37923c6f46dd001000f9acb60dd20a2/image.png?w=480&q=90","srcSet":"//images.ctfassets.net/wdjnw2prxlw8/jkD16NKgOwV1LuqHgzihV/c37923c6f46dd001000f9acb60dd20a2/image.png?w=120&h=60&q=90 120w,\n//images.ctfassets.net/wdjnw2prxlw8/jkD16NKgOwV1LuqHgzihV/c37923c6f46dd001000f9acb60dd20a2/image.png?w=240&h=121&q=90 240w,\n//images.ctfassets.net/wdjnw2prxlw8/jkD16NKgOwV1LuqHgzihV/c37923c6f46dd001000f9acb60dd20a2/image.png?w=480&h=241&q=90 480w,\n//images.ctfassets.net/wdjnw2prxlw8/jkD16NKgOwV1LuqHgzihV/c37923c6f46dd001000f9acb60dd20a2/image.png?w=720&h=362&q=90 720w,\n//images.ctfassets.net/wdjnw2prxlw8/jkD16NKgOwV1LuqHgzihV/c37923c6f46dd001000f9acb60dd20a2/image.png?w=960&h=483&q=90 960w,\n//images.ctfassets.net/wdjnw2prxlw8/jkD16NKgOwV1LuqHgzihV/c37923c6f46dd001000f9acb60dd20a2/image.png?w=1440&h=724&q=90 1440w,\n//images.ctfassets.net/wdjnw2prxlw8/jkD16NKgOwV1LuqHgzihV/c37923c6f46dd001000f9acb60dd20a2/image.png?w=2634&h=1324&q=90 2634w","srcWebp":"//images.ctfassets.net/wdjnw2prxlw8/jkD16NKgOwV1LuqHgzihV/c37923c6f46dd001000f9acb60dd20a2/image.png?w=480&q=90&fm=webp","srcSetWebp":"//images.ctfassets.net/wdjnw2prxlw8/jkD16NKgOwV1LuqHgzihV/c37923c6f46dd001000f9acb60dd20a2/image.png?w=120&h=60&q=90&fm=webp 120w,\n//images.ctfassets.net/wdjnw2prxlw8/jkD16NKgOwV1LuqHgzihV/c37923c6f46dd001000f9acb60dd20a2/image.png?w=240&h=121&q=90&fm=webp 240w,\n//images.ctfassets.net/wdjnw2prxlw8/jkD16NKgOwV1LuqHgzihV/c37923c6f46dd001000f9acb60dd20a2/image.png?w=480&h=241&q=90&fm=webp 480w,\n//images.ctfassets.net/wdjnw2prxlw8/jkD16NKgOwV1LuqHgzihV/c37923c6f46dd001000f9acb60dd20a2/image.png?w=720&h=362&q=90&fm=webp 720w,\n//images.ctfassets.net/wdjnw2prxlw8/jkD16NKgOwV1LuqHgzihV/c37923c6f46dd001000f9acb60dd20a2/image.png?w=960&h=483&q=90&fm=webp 960w,\n//images.ctfassets.net/wdjnw2prxlw8/jkD16NKgOwV1LuqHgzihV/c37923c6f46dd001000f9acb60dd20a2/image.png?w=1440&h=724&q=90&fm=webp 1440w,\n//images.ctfassets.net/wdjnw2prxlw8/jkD16NKgOwV1LuqHgzihV/c37923c6f46dd001000f9acb60dd20a2/image.png?w=2634&h=1324&q=90&fm=webp 2634w","sizes":"(max-width: 480px) 100vw, 480px"}}}},{"node":{"publishDate":"Feb 15, 2022","updatedAt":"Jul 6, 2022","compose__page":[{"title":"Integrating Indoor Positioning System with Mappedin SDKs","slug":"ips-integration"}],"tags":{"category":{"name":"Developers","slug":"developers"},"topic":{"name":"Use Cases","slug":"use-cases"}},"content":{"raw":"{\"data\":{},\"content\":[{\"data\":{\"target\":{\"sys\":{\"id\":\"yV1ZZQhfqbkAoBwXzD0Xn\",\"type\":\"Link\",\"linkType\":\"Asset\"}}},\"content\":[],\"nodeType\":\"embedded-asset-block\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[{\"type\":\"bold\"}],\"value\":\"You are here\",\"nodeType\":\"text\"},{\"data\":{},\"marks\":[],\"value\":\" is one of the first things we expect to find on a map today. There is no need to manually triangulate our position based on stars because the device we hold in our hands can already tell us our location. That is if we are outdoors. The story gets more complicated if we move indoors.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"With Indoor Positioning Systems (IPS) users can see their precise location represented by a blue dot within the context of an indoor map. Similar to outdoor GPS, users are not required to enter a \\\"start\\\" location and as they begin to follow along a route, the blue dot moves with them, providing an enhanced navigational experience.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Indoor Positioning Systems are valuable components of rich indoor mapping experiences. They ensure that the user does not need to know their location in a venue. This saves both time and confusion, making for a much more pleasant wayfinding experience.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"GPS has about 3 meter accuracy when we are outside and have no obstructions such as skyscrapers around us. However, inside a building, GPS is nearly useless. That's why several technologies have been developed to provide indoor positioning in various scenarios such as \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://resources.mappedin.com/blog/what-is-blue-dot-wayfinding\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"wayfinding\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\", \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://resources.mappedin.com/blog/indoor-mapping-use-case-asset-tracking\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"asset tracking\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\", contact monitoring and smart building applications.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Positioning smarts for indoors\",\"nodeType\":\"text\"}],\"nodeType\":\"heading-2\"},{\"data\":{\"target\":{\"sys\":{\"id\":\"4aHfBcHMLcb5o2fmxayffP\",\"type\":\"Link\",\"linkType\":\"Asset\"}}},\"content\":[],\"nodeType\":\"embedded-asset-block\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"The different indoor positioning systems have different tradeoffs. For example, using beacons requires installing Bluetooth Low Energy (BLE) devices in known positions around the venue. WiFi-based systems use existing infrastructure and requires a radio frequency mapping of the venue. Some systems blend multiple technologies in sensor fusion to further process the positioning data into a more accurate representation of the actual position.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"To integrate with the Mappedin SDKs, it does not really matter what underlying technology is used to obtain the indoor position as long as it can provide information about the current floor and the \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mozilla.org/en-US/docs/Web/API/GeolocationCoordinates\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"geocoordinates\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\" (latitude and longitude). Based on that the Mappedin SDK is able to \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/guides/webv4/bluedot\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"place the Blue Dot on the map\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\" indicating the current position of the device.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{\"target\":{\"sys\":{\"id\":\"7tbzSHUglPtWzsIa9kPuUD\",\"type\":\"Link\",\"linkType\":\"Entry\"}}},\"content\":[],\"nodeType\":\"embedded-entry-block\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"One of the easiest ways to \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://resources.mappedin.com/blog/product-101-imdf-export\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"setup Indoor Positioning System is Apple's infrastructure-free positioning\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\" combined with Mappedin map rendering and data management.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Integrating positioning data with Mappedin React Native SDKs\",\"nodeType\":\"text\"}],\"nodeType\":\"heading-2\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Below is the \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/guides/react-native/quickstart/\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"minimal implementation of drawing a map\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\" with Mappedin React Native SDK. In 5 minutes, we will build on this code to display the Blue Dot and override the positions from a premade path of geocoordinates. After ensuring everything works as expected, you can easily connect the IPS to provide the position to Mappedin SDK instead\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{\"target\":{\"sys\":{\"id\":\"aRyiK6XjGEWNpHL0a2E4F\",\"type\":\"Link\",\"linkType\":\"Entry\"}}},\"content\":[],\"nodeType\":\"embedded-entry-block\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"We have pregenerated a route in \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/code/sdks/react-native/positions.json\"},\"content\":[{\"data\":{},\"marks\":[{\"type\":\"code\"},{\"type\":\"bold\"}],\"value\":\"positions.json\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\" that can be used for debugging Blue Dot features in \",\"nodeType\":\"text\"},{\"data\":{},\"marks\":[{\"type\":\"code\"},{\"type\":\"bold\"}],\"value\":\"mappedin-demo-mall\",\"nodeType\":\"text\"},{\"data\":{},\"marks\":[],\"value\":\". Below is the first element of the list, which we will set as our starting coordinate for camera focus. It follows the \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mozilla.org/en-US/docs/Web/API/GeolocationPosition\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"specification of Geolocation Position of Web APIs\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\".\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{\"target\":{\"sys\":{\"id\":\"WWftOy5W8uhstWiXOHYNk\",\"type\":\"Link\",\"linkType\":\"Entry\"}}},\"content\":[],\"nodeType\":\"embedded-entry-block\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"First, we create a \",\"nodeType\":\"text\"},{\"data\":{},\"marks\":[{\"type\":\"code\"},{\"type\":\"bold\"}],\"value\":\"MappedinCoordinate\",\"nodeType\":\"text\"},{\"data\":{},\"marks\":[],\"value\":\" for our starting location and zoom in the camera to get a closer view of the Blue Dot.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{\"target\":{\"sys\":{\"id\":\"4nDiMFiWYwIzJj2E7aOJsU\",\"type\":\"Link\",\"linkType\":\"Entry\"}}},\"content\":[],\"nodeType\":\"embedded-entry-block\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"We set the \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/docs/react-native/latest/enums/state\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"map state\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\" to \",\"nodeType\":\"text\"},{\"data\":{},\"marks\":[{\"type\":\"code\"},{\"type\":\"bold\"}],\"value\":\"follow\",\"nodeType\":\"text\"},{\"data\":{},\"marks\":[],\"value\":\", which keeps the Blue Dot in the middle of the view. Then we enable the Blue Dot and set the starting location.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{\"target\":{\"sys\":{\"id\":\"3gV9owEJDn12XKswCpc1ko\",\"type\":\"Link\",\"linkType\":\"Entry\"}}},\"content\":[],\"nodeType\":\"embedded-entry-block\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"To walk through the list of positions, we can create a simple interval outside of our \",\"nodeType\":\"text\"},{\"data\":{},\"marks\":[{\"type\":\"code\"},{\"type\":\"bold\"}],\"value\":\"render\",\"nodeType\":\"text\"},{\"data\":{},\"marks\":[],\"value\":\" method for overriding the position every few seconds. The position will jump to the start after the route is completed. This code is not optimized and should be replaced with the positioning updates coming from your IPS.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{\"target\":{\"sys\":{\"id\":\"7D6MiKAE2X6B36mYYzcoK2\",\"type\":\"Link\",\"linkType\":\"Entry\"}}},\"content\":[],\"nodeType\":\"embedded-entry-block\"},{\"data\":{\"target\":{\"sys\":{\"id\":\"29dn1eaOYbx5f8K4w1D0Pf\",\"type\":\"Link\",\"linkType\":\"Asset\"}}},\"content\":[],\"nodeType\":\"embedded-asset-block\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Completed sample\",\"nodeType\":\"text\"}],\"nodeType\":\"heading-2\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Below code sample can be pasted into a fresh \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/guides/react-native/\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"React Native starter project\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\" with the addition of \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/code/sdks/react-native/positions.json\"},\"content\":[{\"data\":{},\"marks\":[{\"type\":\"code\"},{\"type\":\"bold\"}],\"value\":\"positions.json\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\".\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{\"target\":{\"sys\":{\"id\":\"7hyVSBE6ShMkx2Qu837G2w\",\"type\":\"Link\",\"linkType\":\"Entry\"}}},\"content\":[],\"nodeType\":\"embedded-entry-block\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Summary\",\"nodeType\":\"text\"}],\"nodeType\":\"heading-2\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Mappedin SDKs are ready to work with any IPS provider that can be integrated with your app to provide geolocation and floor to Mappedin within minutes. The Blue Dot can be enabled and then connected to the position updates in just a few lines of code, delivering accurate indoor positioning and enhanced wayfinding experiences.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"If you want to discuss indoor positioning as it relates to your venue, \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://www.mappedin.com/contact-us/\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"contact us now\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\".\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"}],\"nodeType\":\"blockquote\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"}],\"nodeType\":\"document\"}"},"articleType":"Dev Content","image":{"fluid":{"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAMAAABI111xAAABoVBMVEXR3N/x8fHS0tKQkJCSkpLq6urT0tIOXXXM1djT1NTm5ubCwsK1tbXv7+9Yjp67yc2sr7TQ0dTo6OjE1Nnj4+OqqqrIyMgkbIG5ys/a2tvw8PDr6+uko6Nei5mlu8K8vsLu7u62y9Ht7e07eo2pwcnq6uvm5+jT3N+4y9GcucFGgZNAfI5SiZo6eYwian8MXHSrxMzh5ueJrLYhaoAja4EdZ30ZanoWe4wGXnY/UWVGV2sDV3BmS1wsVGqevMXM1tkdZ3xMWm4MVW4HYXgSbIAXZXsydYlBf5FTippjlaNzn6x/pK+StL5JgZFZY3W0f4kKWHCjvcW+zuTM1+nW1tacnJzl5eXR0NDs7OyFrLjt7u7b4OESXnU5Wm8UVW2MrrioqKje3t63t7d9fX3Ozc1qnKtwn61om6pll6YLWnJXh5bU1NS6urrf39/Pz8+Tk5Pp6enAv79omqkuWW1kTF2Wtr9ZWVnBwMCdvcaQtb/g6u1pm6ocY3gEV3B1nqrFxMTNzMzn5+dlZWXAvr9tnq0laX5HSVxklqTh4eHV1dVVVVWFZ6dkAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH5gkTDzAImbQxwwAAALpJREFUCNdjZGDEAL8Zhd8JMzK+52VlZPzJARZ6JMnGqPhG9IXEv4dKyEpZ+B98UbxvIAQXABr3j4WPgeGHHZDzkwGo+5I+WIIJKPicDwjExAXu8NvtvszHt52XhZchEqJvVfhbPkYGhqOeDAxMPKm8vLz3eHh4GICQJ5/hPy/DKRYmBgYuRkvGGaIcXxg+i/5geM7G8I9FEKK5iJGxk3lJbTdIAxMTz7vXbziBYAoHF98fjnZh+bfhZgDvBy0LpeAB/AAAAABJRU5ErkJggg==","aspectRatio":1.7768817204301075,"src":"//images.ctfassets.net/wdjnw2prxlw8/yV1ZZQhfqbkAoBwXzD0Xn/e605b6b14ed11811c0cb5f995cdd75a0/bluedot-accurate.png?w=480&q=90","srcSet":"//images.ctfassets.net/wdjnw2prxlw8/yV1ZZQhfqbkAoBwXzD0Xn/e605b6b14ed11811c0cb5f995cdd75a0/bluedot-accurate.png?w=120&h=68&q=90 120w,\n//images.ctfassets.net/wdjnw2prxlw8/yV1ZZQhfqbkAoBwXzD0Xn/e605b6b14ed11811c0cb5f995cdd75a0/bluedot-accurate.png?w=240&h=135&q=90 240w,\n//images.ctfassets.net/wdjnw2prxlw8/yV1ZZQhfqbkAoBwXzD0Xn/e605b6b14ed11811c0cb5f995cdd75a0/bluedot-accurate.png?w=480&h=270&q=90 480w,\n//images.ctfassets.net/wdjnw2prxlw8/yV1ZZQhfqbkAoBwXzD0Xn/e605b6b14ed11811c0cb5f995cdd75a0/bluedot-accurate.png?w=720&h=405&q=90 720w,\n//images.ctfassets.net/wdjnw2prxlw8/yV1ZZQhfqbkAoBwXzD0Xn/e605b6b14ed11811c0cb5f995cdd75a0/bluedot-accurate.png?w=960&h=540&q=90 960w,\n//images.ctfassets.net/wdjnw2prxlw8/yV1ZZQhfqbkAoBwXzD0Xn/e605b6b14ed11811c0cb5f995cdd75a0/bluedot-accurate.png?w=1322&h=744&q=90 1322w","srcWebp":"//images.ctfassets.net/wdjnw2prxlw8/yV1ZZQhfqbkAoBwXzD0Xn/e605b6b14ed11811c0cb5f995cdd75a0/bluedot-accurate.png?w=480&q=90&fm=webp","srcSetWebp":"//images.ctfassets.net/wdjnw2prxlw8/yV1ZZQhfqbkAoBwXzD0Xn/e605b6b14ed11811c0cb5f995cdd75a0/bluedot-accurate.png?w=120&h=68&q=90&fm=webp 120w,\n//images.ctfassets.net/wdjnw2prxlw8/yV1ZZQhfqbkAoBwXzD0Xn/e605b6b14ed11811c0cb5f995cdd75a0/bluedot-accurate.png?w=240&h=135&q=90&fm=webp 240w,\n//images.ctfassets.net/wdjnw2prxlw8/yV1ZZQhfqbkAoBwXzD0Xn/e605b6b14ed11811c0cb5f995cdd75a0/bluedot-accurate.png?w=480&h=270&q=90&fm=webp 480w,\n//images.ctfassets.net/wdjnw2prxlw8/yV1ZZQhfqbkAoBwXzD0Xn/e605b6b14ed11811c0cb5f995cdd75a0/bluedot-accurate.png?w=720&h=405&q=90&fm=webp 720w,\n//images.ctfassets.net/wdjnw2prxlw8/yV1ZZQhfqbkAoBwXzD0Xn/e605b6b14ed11811c0cb5f995cdd75a0/bluedot-accurate.png?w=960&h=540&q=90&fm=webp 960w,\n//images.ctfassets.net/wdjnw2prxlw8/yV1ZZQhfqbkAoBwXzD0Xn/e605b6b14ed11811c0cb5f995cdd75a0/bluedot-accurate.png?w=1322&h=744&q=90&fm=webp 1322w","sizes":"(max-width: 480px) 100vw, 480px"}}}},{"node":{"publishDate":"Nov 29, 2021","updatedAt":"Jul 26, 2022","compose__page":[{"title":"Create a leasing map for a mall with Mappedin Web SDK","slug":"create-leasing-app"}],"tags":{"category":{"name":"Developers","slug":"developers"},"topic":{"name":"Use Cases","slug":"use-cases"}},"content":{"raw":"{\"nodeType\":\"document\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Mappedin SDKs are flexible toolkits for working with your venue map to create custom applications beyond consumer-facing indoor navigation. A leasing map is another useful map view for many malls, where potential store owners can view available locations for leasing with more detailed information such as square footage and availability dates.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Augmenting the  map data with an external data source is easy. Locations and polygons have \",\"marks\":[],\"data\":{}},{\"nodeType\":\"text\",\"value\":\"externalId\",\"marks\":[{\"type\":\"code\"}],\"data\":{}},{\"nodeType\":\"text\",\"value\":\" attributes set in the \",\"marks\":[],\"data\":{}},{\"nodeType\":\"hyperlink\",\"data\":{\"uri\":\"https://www.mappedin.com/mapping/map-editor/\"},\"content\":[{\"nodeType\":\"text\",\"value\":\"Mappedin CMS\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"text\",\"value\":\" so that it is seamless to integrate data from other sources on top of the map data rendered by the Mappedin SDK.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"In this blog post we implement some basic functionality for a leasing-focused mall application. We will walk through the following features, which are shown in the embedded demo below. The CodeSandbox demo allows for forking the existing demo for editing. We won't go through every line of code in this post but explain how the key functionalities beyond the basic Mappedin guides are created and could be improved further.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"unordered-list\",\"data\":{},\"content\":[{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Displaying the map (\",\"marks\":[],\"data\":{}},{\"nodeType\":\"hyperlink\",\"data\":{\"uri\":\"https://developer.mappedin.com/guides/webv4/loading-map\"},\"content\":[{\"nodeType\":\"text\",\"value\":\"See guide\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"text\",\"value\":\")\",\"marks\":[],\"data\":{}}]}]},{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Adding availability data\",\"marks\":[],\"data\":{}}]}]},{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Coloring multiple polygons\",\"marks\":[],\"data\":{}}]}]},{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Displaying markers (\",\"marks\":[],\"data\":{}},{\"nodeType\":\"hyperlink\",\"data\":{\"uri\":\"https://developer.mappedin.com/guides/webv4/markers\"},\"content\":[{\"nodeType\":\"text\",\"value\":\"See guide\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"text\",\"value\":\")\",\"marks\":[],\"data\":{}}]}]},{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Focus on a polygon (\",\"marks\":[],\"data\":{}},{\"nodeType\":\"hyperlink\",\"data\":{\"uri\":\"https://developer.mappedin.com/guides/webv4/camera-controls\"},\"content\":[{\"nodeType\":\"text\",\"value\":\"See guide\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"text\",\"value\":\")\",\"marks\":[],\"data\":{}}]}]},{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Listing locations (\",\"marks\":[],\"data\":{}},{\"nodeType\":\"hyperlink\",\"data\":{\"uri\":\"https://developer.mappedin.com/guides/webv4/listing-locations\"},\"content\":[{\"nodeType\":\"text\",\"value\":\"See guide\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"text\",\"value\":\")\",\"marks\":[],\"data\":{}}]}]},{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Filtering locations\",\"marks\":[],\"data\":{}}]}]}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"In the example below, click on a location on the list or on the map to open an informative marker about the unit.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"<iframe src=\\\"https://codesandbox.io/embed/mappedin-leasing-demo-1-p3ri4?fontsize=14&hidenavigation=1&theme=light&view=preview\\\"\\n     style=\\\"width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;\\\"\\n     title=\\\"mappedin-leasing-demo-1\\\"\\n     allow=\\\"accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking\\\"\\n     sandbox=\\\"allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts\\\"\\n   ></iframe>\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"The example uses Typescript and it should be trivial to modify it to Javascript. The \",\"marks\":[],\"data\":{}},{\"nodeType\":\"hyperlink\",\"data\":{\"uri\":\"https://resources.mappedin.com/blog/product-launch-sdk-v-4.0\"},\"content\":[{\"nodeType\":\"text\",\"value\":\"Mappedin Web SDK v4\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"text\",\"value\":\" released in November 2021 provides types so it's natural to take full advantage of them while building applications on top of it.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"heading-2\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Adding availability data\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Availability data is the key to a customer-friendly leasing application. Being able to to display available units and additional information about them requires a data source. Connecting that to the map requires connecting to the source API and making sure the data is in a format that works for your frontend use-case.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"After setting up for the basic setup in the \",\"marks\":[],\"data\":{}},{\"nodeType\":\"text\",\"value\":\"init\",\"marks\":[{\"type\":\"code\"}],\"data\":{}},{\"nodeType\":\"text\",\"value\":\" function (see \",\"marks\":[],\"data\":{}},{\"nodeType\":\"hyperlink\",\"data\":{\"uri\":\"https://developer.mappedin.com/guides/webv4/add-interactivity\"},\"content\":[{\"nodeType\":\"text\",\"value\":\"Adding interactivity -guide\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"text\",\"value\":\"), we move to setting up the availability. In a real service, fetching the availability data could be done before or simultaneously with fetching the venue data. In this example, the available units are created based on the venue data, so we need that first.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"In a real system, the available unit data is requested from a system that manages the leasing data. Additionally, the application could come with authentication to ensure that the leasing data is available only to a limited audience instead of being public. It could also provide richer data about the unit, which is not covered in this example.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"embedded-entry-block\",\"data\":{\"target\":{\"sys\":{\"id\":\"34kUkyVZyQA4tmMPJflvrZ\",\"type\":\"Link\",\"linkType\":\"Entry\"}}},\"content\":[]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Coloring all the polygons is just a loop over the given polygons and setting the color for each of them.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"embedded-entry-block\",\"data\":{\"target\":{\"sys\":{\"id\":\"4YolIoiCt359j2Zc8kpOyu\",\"type\":\"Link\",\"linkType\":\"Entry\"}}},\"content\":[]},{\"nodeType\":\"heading-2\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Displaying markers\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"We created a function for displaying the informational leasing marker.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\" \",\"marks\":[],\"data\":{}},{\"nodeType\":\"text\",\"value\":\"removeAllMarkers() \",\"marks\":[{\"type\":\"code\"}],\"data\":{}},{\"nodeType\":\"text\",\"value\":\"call on the second line of the function makes sure there is only one marker visible at a time by removing all other markers. The marker can be an \",\"marks\":[],\"data\":{}},{\"nodeType\":\"text\",\"value\":\"svg\",\"marks\":[{\"type\":\"code\"}],\"data\":{}},{\"nodeType\":\"text\",\"value\":\" of a pin or any other valid HTML. Therefore, we are able to use a string template and fill it with the (in this case made up) leasing information.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"embedded-entry-block\",\"data\":{\"target\":{\"sys\":{\"id\":\"1tLdMRDNws5Lx8g4vJiNnZ\",\"type\":\"Link\",\"linkType\":\"Entry\"}}},\"content\":[]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"The styling of the marker is easy to customize with CSS:\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"embedded-entry-block\",\"data\":{\"target\":{\"sys\":{\"id\":\"1SUmJhszfuhSEE5fEDD63D\",\"type\":\"Link\",\"linkType\":\"Entry\"}}},\"content\":[]},{\"nodeType\":\"heading-2\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Listing and filtering locations\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"The demo implements a single checkbox for filtering how the location list is displayed. Our \",\"marks\":[],\"data\":{}},{\"nodeType\":\"text\",\"value\":\"populateLocationList(onlyShowAvailable = false)\",\"marks\":[{\"type\":\"code\"}],\"data\":{}},{\"nodeType\":\"text\",\"value\":\" function takes a boolean input for either displaying all of the locations or only the ones that are marked available. We also set \",\"marks\":[],\"data\":{}},{\"nodeType\":\"text\",\"value\":\"onChange\",\"marks\":[{\"type\":\"code\"}],\"data\":{}},{\"nodeType\":\"text\",\"value\":\" event handler for the checkbox to first clear and then repopulate the location list based on the new filter value. In our loop to create the location list elements, we check the filter and availability data, to make sure only the desired locations are shown.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"embedded-entry-block\",\"data\":{\"target\":{\"sys\":{\"id\":\"AKbNomDcMFE00wEo1C9mN\",\"type\":\"Link\",\"linkType\":\"Entry\"}}},\"content\":[]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"This code is very specific to this one filter. However,\\n with richer data coming from a leasing system this could be customized \\nfurther. For example, it could be possible to filter to show only the \\nanchor stores or to display units larger than a threshold area.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"heading-2\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"What's next?\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"To take this example to the next level, it could use more filters that interactively highlight polygons on the map, a search option and richer markers based on availability data coming from a real-world system.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"blockquote\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Did this post help you implement a leasing application? Which example would you like us to build next? Let us know on \",\"marks\":[],\"data\":{}},{\"nodeType\":\"hyperlink\",\"data\":{\"uri\":\"https://twitter.com/mappedin\"},\"content\":[{\"nodeType\":\"text\",\"value\":\"Twitter\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"text\",\"value\":\"\",\"marks\":[],\"data\":{}}]}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"\",\"marks\":[],\"data\":{}}]}]}"},"articleType":"Dev Content","image":{"fluid":{"base64":"data:image/png;base64,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","aspectRatio":1.589307411907655,"src":"//images.ctfassets.net/wdjnw2prxlw8/3KUijAcYOygyu9sDfmRbJw/c34a546da3590d0c5775f15561d80477/image.png?w=480&q=90","srcSet":"//images.ctfassets.net/wdjnw2prxlw8/3KUijAcYOygyu9sDfmRbJw/c34a546da3590d0c5775f15561d80477/image.png?w=120&h=76&q=90 120w,\n//images.ctfassets.net/wdjnw2prxlw8/3KUijAcYOygyu9sDfmRbJw/c34a546da3590d0c5775f15561d80477/image.png?w=240&h=151&q=90 240w,\n//images.ctfassets.net/wdjnw2prxlw8/3KUijAcYOygyu9sDfmRbJw/c34a546da3590d0c5775f15561d80477/image.png?w=480&h=302&q=90 480w,\n//images.ctfassets.net/wdjnw2prxlw8/3KUijAcYOygyu9sDfmRbJw/c34a546da3590d0c5775f15561d80477/image.png?w=720&h=453&q=90 720w,\n//images.ctfassets.net/wdjnw2prxlw8/3KUijAcYOygyu9sDfmRbJw/c34a546da3590d0c5775f15561d80477/image.png?w=960&h=604&q=90 960w,\n//images.ctfassets.net/wdjnw2prxlw8/3KUijAcYOygyu9sDfmRbJw/c34a546da3590d0c5775f15561d80477/image.png?w=1308&h=823&q=90 1308w","srcWebp":"//images.ctfassets.net/wdjnw2prxlw8/3KUijAcYOygyu9sDfmRbJw/c34a546da3590d0c5775f15561d80477/image.png?w=480&q=90&fm=webp","srcSetWebp":"//images.ctfassets.net/wdjnw2prxlw8/3KUijAcYOygyu9sDfmRbJw/c34a546da3590d0c5775f15561d80477/image.png?w=120&h=76&q=90&fm=webp 120w,\n//images.ctfassets.net/wdjnw2prxlw8/3KUijAcYOygyu9sDfmRbJw/c34a546da3590d0c5775f15561d80477/image.png?w=240&h=151&q=90&fm=webp 240w,\n//images.ctfassets.net/wdjnw2prxlw8/3KUijAcYOygyu9sDfmRbJw/c34a546da3590d0c5775f15561d80477/image.png?w=480&h=302&q=90&fm=webp 480w,\n//images.ctfassets.net/wdjnw2prxlw8/3KUijAcYOygyu9sDfmRbJw/c34a546da3590d0c5775f15561d80477/image.png?w=720&h=453&q=90&fm=webp 720w,\n//images.ctfassets.net/wdjnw2prxlw8/3KUijAcYOygyu9sDfmRbJw/c34a546da3590d0c5775f15561d80477/image.png?w=960&h=604&q=90&fm=webp 960w,\n//images.ctfassets.net/wdjnw2prxlw8/3KUijAcYOygyu9sDfmRbJw/c34a546da3590d0c5775f15561d80477/image.png?w=1308&h=823&q=90&fm=webp 1308w","sizes":"(max-width: 480px) 100vw, 480px"}}}},{"node":{"publishDate":"Jul 28, 2020","updatedAt":"Jul 6, 2022","compose__page":[{"title":"Open Source Contact Monitoring","slug":"contact-monitoring"}],"tags":{"category":{"name":"Developers","slug":"developers"},"topic":{"name":"Use Cases","slug":"use-cases"}},"content":{"raw":"{\"data\":{},\"content\":[{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"We are happy to share that we have open sourced our Contact Monitoring solution! To see how it all works under the hood and get your own system running, read on.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Why\",\"nodeType\":\"text\"}],\"nodeType\":\"heading-2\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"\",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://www.apple.com/covid19/contacttracing\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Apple and Google has teamed up together\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\" to offer a set of public contact tracing APIs that allows iOS and Android devices to be used to detect when an individual has been in contact with some who has been tested positive for COVID-19. While governments and health authorities around the world are benefiting from this technology, very little is available for the enterprise world, where employees are coming back to work, places are re-opening. With our contact monitoring solution, we can utilize indoor positioning technologies, such as \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.apple.com/videos/play/wwdc2019/245/\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Apple's Indoor Maps Program\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\", and use that information to accurately determine contact events. We've been \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://info.mappedin.com/apple-maps\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"working with Apple\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\" for many years, building on top this positioning technology. It would naturally be a fit here. A solution like this would benefit places like hospitals, corporate offices, manufacturing facilities, etc.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[{\"type\":\"bold\"}],\"value\":\"Privacy\",\"nodeType\":\"text\"},{\"data\":{},\"marks\":[],\"value\":\" is an area where we feel full transparency is the ultimate solution. There's nothing better than showing all of the code that's involved in processing the data. This is why we have decided to \",\"nodeType\":\"text\"},{\"data\":{},\"marks\":[{\"type\":\"bold\"}],\"value\":\"open source\",\"nodeType\":\"text\"},{\"data\":{},\"marks\":[],\"value\":\" our solution. We've done our best to ensure there's no personal information being collected. We used generated IDs everywhere, only send data when you are on site at your venue, and you have the full power on deciding how long to retain the data in the database. You can read our documentation on \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://github.com/mappedin/contact-monitoring-ingest-api\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"github\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\" for more information.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Overview\",\"nodeType\":\"text\"}],\"nodeType\":\"heading-2\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Below is a diagram that shows how the contact monitoring solution works.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{\"target\":{\"sys\":{\"id\":\"CDwcDKa0753SBqmxlmjpn\",\"type\":\"Link\",\"linkType\":\"Asset\"}}},\"content\":[],\"nodeType\":\"embedded-asset-block\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"We have created a \",\"nodeType\":\"text\"},{\"data\":{},\"marks\":[{\"type\":\"bold\"}],\"value\":\"mobile sdk\",\"nodeType\":\"text\"},{\"data\":{},\"marks\":[],\"value\":\" that anyone can integrate with their existing employee app. It continuously collects positioning data, but only when the device is located on site. The data is then sent to the server for processing. The server processes the events in near realtime and determines contact. And finally, we made a dashboard that can visualize the contact events.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"There are mainly 3 components that we are open sourcing, \",\"nodeType\":\"text\"},{\"data\":{},\"marks\":[{\"type\":\"bold\"}],\"value\":\"Mobile SDK\",\"nodeType\":\"text\"},{\"data\":{},\"marks\":[],\"value\":\", \",\"nodeType\":\"text\"},{\"data\":{},\"marks\":[{\"type\":\"bold\"}],\"value\":\"Ingestion API\",\"nodeType\":\"text\"},{\"data\":{},\"marks\":[],\"value\":\", and \",\"nodeType\":\"text\"},{\"data\":{},\"marks\":[{\"type\":\"bold\"}],\"value\":\"Dashboard\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Native Mobile SDK\",\"nodeType\":\"text\"}],\"nodeType\":\"heading-3\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"This SDK is responsible for tapping into indoor positioning systems to get device's location data, and it does so in the background in a way that minimizes battery usage. It only collects position data \",\"nodeType\":\"text\"},{\"data\":{},\"marks\":[{\"type\":\"bold\"}],\"value\":\"while you are on site\",\"nodeType\":\"text\"},{\"data\":{},\"marks\":[],\"value\":\". Testing in our lab indicates that we consistently get accuracy of 2 meters (or 6 feet) when using Apple's Indoor Positioning System. \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://www.mappedin.com/contact-us/\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Contact us\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\" to get your venue indoor positioning ready.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"To learn more about how our Contact Monitoring Mobile SDK works, \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/blog/2020-07-27-contact-monitoring-mobile/\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"go here\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\".\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Ingestion API\",\"nodeType\":\"text\"}],\"nodeType\":\"heading-3\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"The Ingestion API is where the heart of the system is. It is responsible for collecting device location data, process it at high volume to determine contact, and store it optimally for querying by our dashboard. We went through several iterations of the design of the algorithm. You can \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.mappedin.com/blog/2020-07-28-contact-monitoring-api/\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"read about it here\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\".\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Dashboard\",\"nodeType\":\"text\"}],\"nodeType\":\"heading-3\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"The dashboard is where all the contact monitoring information is surfaced. It queries the processed data and provides visualizations that uses Mappedin's indoor maps. \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://www.mappedin.com/contact-us/\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Contact us\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\" to get a map created for you.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{\"target\":{\"sys\":{\"id\":\"VAtrLwrNUbT1vB4g0ADSU\",\"type\":\"Link\",\"linkType\":\"Asset\"}}},\"content\":[],\"nodeType\":\"embedded-asset-block\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Some functionalities of the dashboard include:\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"content\":[{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"List detail view & map detail view\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"}],\"nodeType\":\"list-item\"},{\"data\":{},\"content\":[{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Contact events are aggregated as you zoom out of the map and disperse as you zoom in\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"}],\"nodeType\":\"list-item\"},{\"data\":{},\"content\":[{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"When an event is selected, a summary of that event displays\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"}],\"nodeType\":\"list-item\"},{\"data\":{},\"content\":[{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"An event is placed at the location where the interaction started\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"}],\"nodeType\":\"list-item\"}],\"nodeType\":\"unordered-list\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Get started on running your own Contact Monitoring system\",\"nodeType\":\"text\"}],\"nodeType\":\"heading-3\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Head on over to our github pages.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"\",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://github.com/mappedin/contact-monitoring-ios-sdk\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"iOS Contact Monitoring SDK\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\"\\n\",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://github.com/mappedin/contact-monitoring-ingest-api\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Contact Monitoring Ingest API\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\"\\n\",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://github.com/mappedin/contact-monitoring-dashboard\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Contact Monitoring Dashboard\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\"\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"}],\"nodeType\":\"document\"}"},"articleType":"Dev Content","image":{"fluid":{"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAIAAACgpqunAAABPGlDQ1BpY2MAAHjarY6/SwJhGICfLy0tkRout+DAQQKNuhyKhjAHCRpEBH9MneelwWUf50EFtUV/QrS0NrU1SJv0FwRFRXO0RYPgknINV2lLU8/yPjy88L4wMqVLafmBnYZj5zJrarFUVgOvjDFDkCViutGUqWx2A+B7/qb7gAC4S+hSWuel25Pc1crb9GRo9V0mqvzNRNVsGkAPMAxpOyA2geieIx0Qx4BiF0tlEGeAUvP8ElAqnrcBxc7n0iDugSmjrldBvADxylCvDfnXXYBxLZnRCguZJP+MY+47AOldeWBv1+qOmpLSMtX1hjEXV7V5TYNiqax6250nBCAiwUE7jMLyEfjMQcu14DoC4eagxXoQtuAmL3Vb/3lAdP3NrUXN81ALRk9dt1OAwCz0H133o+W6/QvwPUO7+wmwI11jwHnLJwAAAAlwSFlzAAAXEQAAFxEByibzPwAAAAd0SU1FB+YJHRMcNHoJC80AAALDSURBVBgZLcHNbhtVFADge8+5d+bOjMeOHedPlZJCSOQqZNFs+gRFLNjwCDwIW7Y8AjsWIBBrWCNVQk0jUkVNnJLUqR07jV2Pxx7P3L9DhPg+/tvPPzHG4jQlxr797vsFNrPJhMcpk+jyjJVzZpec3DdfPz980un1ej/+8mtVaeOd9x6CKAqUkoGK4vSo8wnFzVAgEaEKHYGXMQtSFjVP374n76y1CICIAvABEBcoAhAhSrWz27HjG4wbMQrtAh4lJEISISM6vezJuBYltSSJlQoFIgDgTf/24OAQhPz9z1d348V00NXJBhVZqGKspcwZQOaN8Z5troSb62sPVtttISVjhKsh5OP7nbuTH/74azgadXa37nRQzKZa67jRBO78g6pk3i/z6dHnHQ7w+s15t9s1VQmPW0kxvH47HO3Vqmd77S++/GrxoWedcU7bRWGlImNkoKJAdnvDj1l2enb28vjVbL4YZ7mYLG0SULY0SRztP+gcNLAarT7i40ExGQA54GSNseQ4Bi9enlx0L7Qx3nvGGNTraUMFl/dFAk4KNI4O93dRSCAbpC259yRuNUSiUIVciOOzi1k+5+x/wAEc0Woky7LM8/nJ+U1z61NY3HOV6OmtuTrXBMwTLwsyVV4sgRHnnP1HIACHAAUPOBv134+TEYbpip/PMPZ67mZ5OdLMWylQoADgSVrnWBjnnDHCGUORcuC9NdVsukKOQGyvNa+6Z6FSGkNHIYHQlWaMEDwRhUpFKAX3oh7JOIRWq72cz0pmTdZn1n+2/XizFtx9nFjrOOdRFAkhrLXaaADw1mljrLUi54qC5PjvS2JMG/vo+t3+1lrIHM6X//QntVZ7e3tnY329d311O+g3Go3B7VBKUZYVcM6Pnj6VUs6LpVKKMaa1cc467x15ZwxwDhyEwHZzJa3X1zc2sywbDAaV1t77fwFCDootIOUo1AAAABJ0RVh0ZXhpZjpFeGlmT2Zmc2V0ADI2UxuiZQAAABl0RVh0ZXhpZjpQaXhlbFhEaW1lbnNpb24ANTU3NXEcx3AAAAAZdEVYdGV4aWY6UGl4ZWxZRGltZW5zaW9uADM5MjNwgutQAAAAKHRFWHRpY2M6Y29weXJpZ2h0AENvcHlyaWdodCBBcHBsZSBJbmMuLCAyMDIy5LS/nAAAABh0RVh0aWNjOmRlc2NyaXB0aW9uADI0RzJXMUc0qa0vjAAAAABJRU5ErkJggg==","aspectRatio":1.4211062962018863,"src":"//images.ctfassets.net/wdjnw2prxlw8/46WIUSrjNwDxcBLgYdaXWL/6119e327ffa037fccc07d089b031ff8a/image.png?w=480&q=90","srcSet":"//images.ctfassets.net/wdjnw2prxlw8/46WIUSrjNwDxcBLgYdaXWL/6119e327ffa037fccc07d089b031ff8a/image.png?w=120&h=84&q=90 120w,\n//images.ctfassets.net/wdjnw2prxlw8/46WIUSrjNwDxcBLgYdaXWL/6119e327ffa037fccc07d089b031ff8a/image.png?w=240&h=169&q=90 240w,\n//images.ctfassets.net/wdjnw2prxlw8/46WIUSrjNwDxcBLgYdaXWL/6119e327ffa037fccc07d089b031ff8a/image.png?w=480&h=338&q=90 480w,\n//images.ctfassets.net/wdjnw2prxlw8/46WIUSrjNwDxcBLgYdaXWL/6119e327ffa037fccc07d089b031ff8a/image.png?w=720&h=507&q=90 720w,\n//images.ctfassets.net/wdjnw2prxlw8/46WIUSrjNwDxcBLgYdaXWL/6119e327ffa037fccc07d089b031ff8a/image.png?w=960&h=676&q=90 960w,\n//images.ctfassets.net/wdjnw2prxlw8/46WIUSrjNwDxcBLgYdaXWL/6119e327ffa037fccc07d089b031ff8a/image.png?w=1440&h=1013&q=90 1440w","srcWebp":"//images.ctfassets.net/wdjnw2prxlw8/46WIUSrjNwDxcBLgYdaXWL/6119e327ffa037fccc07d089b031ff8a/image.png?w=480&q=90&fm=webp","srcSetWebp":"//images.ctfassets.net/wdjnw2prxlw8/46WIUSrjNwDxcBLgYdaXWL/6119e327ffa037fccc07d089b031ff8a/image.png?w=120&h=84&q=90&fm=webp 120w,\n//images.ctfassets.net/wdjnw2prxlw8/46WIUSrjNwDxcBLgYdaXWL/6119e327ffa037fccc07d089b031ff8a/image.png?w=240&h=169&q=90&fm=webp 240w,\n//images.ctfassets.net/wdjnw2prxlw8/46WIUSrjNwDxcBLgYdaXWL/6119e327ffa037fccc07d089b031ff8a/image.png?w=480&h=338&q=90&fm=webp 480w,\n//images.ctfassets.net/wdjnw2prxlw8/46WIUSrjNwDxcBLgYdaXWL/6119e327ffa037fccc07d089b031ff8a/image.png?w=720&h=507&q=90&fm=webp 720w,\n//images.ctfassets.net/wdjnw2prxlw8/46WIUSrjNwDxcBLgYdaXWL/6119e327ffa037fccc07d089b031ff8a/image.png?w=960&h=676&q=90&fm=webp 960w,\n//images.ctfassets.net/wdjnw2prxlw8/46WIUSrjNwDxcBLgYdaXWL/6119e327ffa037fccc07d089b031ff8a/image.png?w=1440&h=1013&q=90&fm=webp 1440w","sizes":"(max-width: 480px) 100vw, 480px"}}}},{"node":{"publishDate":"Jul 27, 2020","updatedAt":"Jul 6, 2022","compose__page":[{"title":"Contact Monitoring Mobile SDK","slug":"contact-monitoring-mobile"}],"tags":{"category":{"name":"Developers","slug":"developers"},"topic":{"name":"Features","slug":"features"}},"content":{"raw":"{\"data\":{},\"content\":[{\"data\":{\"target\":{\"sys\":{\"id\":\"6GR6c7RDo0k25R3UUAEj3n\",\"type\":\"Link\",\"linkType\":\"Asset\"}}},\"content\":[],\"nodeType\":\"embedded-asset-block\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"We spent some time designing a mobile SDK that securely sends positioning data to our contact monitoring ingestion API. The SDK does it silently in the background, preserving battery, and sends indoor positioning data securely to our backend server. The following are the challenges we encountered when building out the SDK, specifically regarding user privacy.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Problem\",\"nodeType\":\"text\"}],\"nodeType\":\"heading-2\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[{\"type\":\"bold\"}],\"value\":\"Protecting user privacy:\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"One of the main challenges of this SDK was ensuring user privacy. According to \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://developer.apple.com/documentation/uikit/protecting_the_user_s_privacy\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Apple's privacy guidelines\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\", an app should only request access only when the app needs the data. However, due to the nature of contact monitoring requiring constant background location updates to be actually useful, the user is required to allow permissions so that the app always provides location updates in the background.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"To protect user privacy, we needed to use the minimum amount of data required which meant only storing and sending locations to the server when the user is actually at the venue. Fortunately for us at Mappedin we specialize in venue positioning data allowing us to accurately determine if the user is actually in the venue!\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Approach\",\"nodeType\":\"text\"}],\"nodeType\":\"heading-2\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"All venues generated by our mapping team in our Mappedin CMS are able to be exported in a specialized GeoJSON format called Mappedin Venue Format (MVF) which contains building and room coordinates from our CMS.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{\"target\":{\"sys\":{\"id\":\"4RxfOr6fIUx1E3RRn3yoOq\",\"type\":\"Link\",\"linkType\":\"Asset\"}}},\"content\":[],\"nodeType\":\"embedded-asset-block\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Through our SDK, we request the venue's MVF and use the data to calculate the geometry of the venue as a polygon. Additionally, a geofence region is calculated using a \",\"nodeType\":\"text\"},{\"data\":{\"uri\":\"https://www.nayuki.io/page/smallest-enclosing-circle\"},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"minimum bounding circle algorithm\",\"nodeType\":\"text\"}],\"nodeType\":\"hyperlink\"},{\"data\":{},\"marks\":[],\"value\":\". This geofence region is used to toggle locations updates, which prevents the SDK from receiving location updates in the background when the device is outside the geofence region and only turn it on when they are in the geofence region. This also helps minimize battery usage.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{\"target\":{\"sys\":{\"id\":\"5yltme6MLjJALtypENJscT\",\"type\":\"Link\",\"linkType\":\"Asset\"}}},\"content\":[],\"nodeType\":\"embedded-asset-block\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Once location updates are turned on, the SDK will check each coordinate to determine if it is inside the venue. If it is in the venue, the location will be stored until a certain amount of time has elapsed (default is 5 minutes). It is then sent to the server in batches. This is done to prevent the number of requests sent to the server. Once the request succeeds, the locations will be \",\"nodeType\":\"text\"},{\"data\":{},\"marks\":[{\"type\":\"bold\"}],\"value\":\"deleted\",\"nodeType\":\"text\"},{\"data\":{},\"marks\":[],\"value\":\" from the device.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"So what do we store?\",\"nodeType\":\"text\"}],\"nodeType\":\"heading-3\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"There are two CoreData entities in our SDK, Device and Location.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[{\"type\":\"bold\"}],\"value\":\"Device Attributes\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{\"target\":{\"sys\":{\"id\":\"2rXs96snYNWCuKfBgLmrOZ\",\"type\":\"Link\",\"linkType\":\"Entry\"}}},\"content\":[],\"nodeType\":\"embedded-entry-block\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[{\"type\":\"bold\"}],\"value\":\"Location Attributes\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{\"target\":{\"sys\":{\"id\":\"6lOwnNAXRJARUvY1Ejd1lv\",\"type\":\"Link\",\"linkType\":\"Entry\"}}},\"content\":[],\"nodeType\":\"embedded-entry-block\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"The device type attribute is the model of the phone and the id is a unique UUID generated the first time the SDK starts tracking the user's position. This identifier does not provide any way to determine who the user actually is and non-persistent when the app is uninstalled. The only meaningful data that may provide some identification of the user is the type attribute.\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Conclusion\",\"nodeType\":\"text\"}],\"nodeType\":\"heading-2\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"Maintaining user privacy is a difficult challenge when it comes to contact monitoring, however it is an essential problem to solve. If the user doesn't trust the app with their data then they're unlikely to want to install the app. Well you could enforce it on employees making it mandatory if they want to work but that level of distrust would probably only make things worse. At Mappedin we believe it is important to maintain full transparency into how your app will store and use their location data and we hope that you do the same if you're involved in the development of any of the numerous contact monitoring projects!\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"},{\"data\":{\"target\":{\"sys\":{\"id\":\"R7JJeEcRrTZHJroRgLZU7\",\"type\":\"Link\",\"linkType\":\"Asset\"}}},\"content\":[],\"nodeType\":\"embedded-asset-block\"},{\"data\":{},\"content\":[{\"data\":{},\"marks\":[],\"value\":\"\",\"nodeType\":\"text\"}],\"nodeType\":\"paragraph\"}],\"nodeType\":\"document\"}"},"articleType":"Dev Content","image":{"fluid":{"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAMAAACejr5sAAABN2lDQ1BpY2MAAHjaY2BgEkgsKMhhYWBgyM0rKQpyd1KIiIxSYH/GwMYgy8DBYMGgnphcXOAYEOADVMIAo1HBt2sMjCD6si7IrLmRF9qCNlu/FeXntn9foJvCgB9wpaQWJwPpP0CcnFxQVMLAwJgAZCuXlxSA2C1AtkgR0FFA9gwQOx3CXgNiJ0HYB8BqQoKcgewrQLZAckYi0F7GJ0C2ThKSeDoSG2ovCHAambgbhRu6mzBQGZSkVpSAaOf8gsqizPSMEgVHYAilKnjmJevpKBgZGBkxMIDCG6L68w1wODKKcSDEapQZGCxrGRiYUxFiQdsZGHaKMTDwFiPE1IHhx5vDwHAopCCxKBHuAMZvLMVpxkYQNjdQH+u0//8/hzMwsGsyMPy9/v//7+3///9dBjT/FgPDgW8AsCNdY3Mb3eoAAAL9UExURV81I08sJD8kJTQhJS0gJSkfJSgeJSgeJicfJiYhKSYkLiQnMCMnLSMjKSIgJiEfJSEeJSAeJR8eJG49Il4zJFEsJ0AmJTQiJSwgJSogJSkhKSklLCgnLycoMyYnMCYiKSUgJiMgJiIgJSIfJSAfJXxFInI+Kms9Ol86LT0mJjQkJy4kKywmLysoMSsmLisiKSohKCghKCchJyUhJyMhJiIhJiQhJigiJodMI4JKJXNDKFo0J0wvKkAsLjInLjAmLjkpKTAxOy4jKS0jKCokKCkjKCckKTQqJ0gzJEkyJDQoJYxQI31FJHFAJWM7J1Q1LUEsLTUuMzJRYT4vLzMmLDElKy8lKy0mKkA0KUM7ME47KEk4Jy4nKCQkKCMjKI5SI4JKI3dFJGc7K2NBRU1EUTksMTYmLjYlLTUnKzYqLFlGKzVAREI4LTEuLCkpKycoKiUnKSQmKSQlKY9TI4VLKoRAO3QyRVg0OEEsLDkoLjopL1hBLFRBKj80MTgwLjEuLi4uLSsuLCkuLSctLSYxNiU8SSFYc45TJIxRSpJfQGtELk40L0c1N1o8KT01O0IyLkE/QENDTDQyLjI0Li82LixBQChdaiSKoiOLsCRwmSJypYpQI41YKoFUMHdTSl5NUz82QT1MWDxebD9ZYUBHRDs4MVJKLkBnaTV6iEyAhStskySTuSVwkyNlhyVLYYFKI5NbI5RgKYBaPm9YVEQ6RDo9QjtDS1VLNldMLW1dLYBpLGNaMXZpLlJWMCxLOyxKPDpFMUFAOiY9OnNAJHdEL3VUMHJLKGdNQkBET0xdW1FRRVNHLlFMLENKLjVKMTdRMU5YMVxcMH1iMXdYO2JUTz9QdyVJYGI3I1cyJU0vJkIrJz4+TD9FST41MzoxK0VAK09NLE9TLm1nNGBvTlN2bTtwji5tnShjjiRehCVYbiVRSFAtJEYqJD0nJTYlJjo0QEA1L04/MkhST0ZdVT9fWzNiYTNpdDJ1gypqiSppVSlpOSdmNCZiMyVcMyVXMrwFrQwAAAAJcEhZcwAAFxEAABcRAcom8z8AAAAHdElNRQfmCR0THDR6CQvNAAAAKElEQVQY02NkYMQADIzC6EJvRBlYhNCVAQVYhNG1MmIKggGLyNAQBAAKegMd9bG8wwAAABJ0RVh0ZXhpZjpFeGlmT2Zmc2V0ADI2UxuiZQAAABl0RVh0ZXhpZjpQaXhlbFhEaW1lbnNpb24ANTE4NIaKQ/UAAAAZdEVYdGV4aWY6UGl4ZWxZRGltZW5zaW9uADM0NTZHcRpLAAAAKHRFWHRpY2M6Y29weXJpZ2h0AENvcHlyaWdodCBBcHBsZSBJbmMuLCAyMDIy5LS/nAAAABh0RVh0aWNjOmRlc2NyaXB0aW9uADI0RzJXMUc0qa0vjAAAAABJRU5ErkJggg==","aspectRatio":1.5,"src":"//images.ctfassets.net/wdjnw2prxlw8/3NqbEg7NNUUTl8FzRyamW7/83f740a88d0e40f8ad898416da763739/image.png?w=480&q=90","srcSet":"//images.ctfassets.net/wdjnw2prxlw8/3NqbEg7NNUUTl8FzRyamW7/83f740a88d0e40f8ad898416da763739/image.png?w=120&h=80&q=90 120w,\n//images.ctfassets.net/wdjnw2prxlw8/3NqbEg7NNUUTl8FzRyamW7/83f740a88d0e40f8ad898416da763739/image.png?w=240&h=160&q=90 240w,\n//images.ctfassets.net/wdjnw2prxlw8/3NqbEg7NNUUTl8FzRyamW7/83f740a88d0e40f8ad898416da763739/image.png?w=480&h=320&q=90 480w,\n//images.ctfassets.net/wdjnw2prxlw8/3NqbEg7NNUUTl8FzRyamW7/83f740a88d0e40f8ad898416da763739/image.png?w=720&h=480&q=90 720w,\n//images.ctfassets.net/wdjnw2prxlw8/3NqbEg7NNUUTl8FzRyamW7/83f740a88d0e40f8ad898416da763739/image.png?w=960&h=640&q=90 960w,\n//images.ctfassets.net/wdjnw2prxlw8/3NqbEg7NNUUTl8FzRyamW7/83f740a88d0e40f8ad898416da763739/image.png?w=1440&h=960&q=90 1440w","srcWebp":"//images.ctfassets.net/wdjnw2prxlw8/3NqbEg7NNUUTl8FzRyamW7/83f740a88d0e40f8ad898416da763739/image.png?w=480&q=90&fm=webp","srcSetWebp":"//images.ctfassets.net/wdjnw2prxlw8/3NqbEg7NNUUTl8FzRyamW7/83f740a88d0e40f8ad898416da763739/image.png?w=120&h=80&q=90&fm=webp 120w,\n//images.ctfassets.net/wdjnw2prxlw8/3NqbEg7NNUUTl8FzRyamW7/83f740a88d0e40f8ad898416da763739/image.png?w=240&h=160&q=90&fm=webp 240w,\n//images.ctfassets.net/wdjnw2prxlw8/3NqbEg7NNUUTl8FzRyamW7/83f740a88d0e40f8ad898416da763739/image.png?w=480&h=320&q=90&fm=webp 480w,\n//images.ctfassets.net/wdjnw2prxlw8/3NqbEg7NNUUTl8FzRyamW7/83f740a88d0e40f8ad898416da763739/image.png?w=720&h=480&q=90&fm=webp 720w,\n//images.ctfassets.net/wdjnw2prxlw8/3NqbEg7NNUUTl8FzRyamW7/83f740a88d0e40f8ad898416da763739/image.png?w=960&h=640&q=90&fm=webp 960w,\n//images.ctfassets.net/wdjnw2prxlw8/3NqbEg7NNUUTl8FzRyamW7/83f740a88d0e40f8ad898416da763739/image.png?w=1440&h=960&q=90&fm=webp 1440w","sizes":"(max-width: 480px) 100vw, 480px"}}}},{"node":{"publishDate":"Jul 27, 2020","updatedAt":"Jul 6, 2022","compose__page":[{"title":"Contact Monitoring Ingestion API","slug":"contact-monitoring-api"}],"tags":{"category":{"name":"Developers","slug":"developers"},"topic":{"name":"Features","slug":"features"}},"content":{"raw":"{\"nodeType\":\"document\",\"data\":{},\"content\":[{\"nodeType\":\"embedded-asset-block\",\"data\":{\"target\":{\"sys\":{\"id\":\"5wLc7oWRsgH6o8dSX0wv2d\",\"type\":\"Link\",\"linkType\":\"Asset\"}}},\"content\":[]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"We've spent several iterations figuring out how to solve the problem of determining two devices that are within 5 meters of each another for a minimum duration of 5 minutes. That's how we define a \\\"contact\\\" event. This is the thought process and the challenges we faced when designing the system.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"heading-2\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Problem\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Input into the system: \",\"marks\":[{\"type\":\"bold\"}],\"data\":{}},{\"nodeType\":\"text\",\"value\":\"Spatial and temporal data of devices that indicates the position of a device, accuracy radius of the position in meters, timestamp when the position was recorded.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"For example:\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"embedded-entry-block\",\"data\":{\"target\":{\"sys\":{\"id\":\"3Z9c76XwLQtTcPWiMWC5gR\",\"type\":\"Link\",\"linkType\":\"Entry\"}}},\"content\":[]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Query\",\"marks\":[{\"type\":\"bold\"}],\"data\":{}},{\"nodeType\":\"text\",\"value\":\": Given a device id, a timed window represented by a start and end date time, and a proximity in meters (5m in requirement).\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Output\",\"marks\":[{\"type\":\"bold\"}],\"data\":{}},{\"nodeType\":\"text\",\"value\":\": A list of devices along with their spatial and temporal data that were \\ndeemed to have contacted, ie. come within the proximity of, while taking the accuracy radius into account, the queried device, for 5 minutes or more.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"heading-2\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Problem Breakdown\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Since we want to be able to show how many contacts each device/person has, this pretty much requires that the proximity relationships between everyone and everyone else be aggregated at run time.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"The problem can then possibly be broken down into 2 parts.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"ordered-list\",\"data\":{},\"content\":[{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Normalizing each piece of position by time and space, since\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"ordered-list\",\"data\":{},\"content\":[{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"the location data is not collected at exactly the same time between all devices, we can only determine contact by loosely matching on fixed time window\",\"marks\":[],\"data\":{}}]}]},{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"reported position is not guaranteed to be always accurate, some can be discard if they have too low of accuracy \",\"marks\":[],\"data\":{}}]}]}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"The purpose is to return a list of contact relationships (who is in proximity of whom in a given time window) between two devices. \",\"marks\":[],\"data\":{}}]}]},{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Given the list of contact relationships between any two devices in each time window, track and aggregate them, while keeping track of the duration of the aggregation.\",\"marks\":[],\"data\":{}}]}]}]},{\"nodeType\":\"heading-2\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Approaches\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Due to potentially significantly large number of position data being ingested (1000 employees at a venue each updating location once a minute = 1000 * 60 * 24 = 1.44 million position records a day), it is not realistic for the system to query such a large dataset directly. We’d have to aggregate this data first.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"heading-3\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Universal Grid Approach\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"First from the spatial perspective, model the world as a large map and create a grid using WGS84 coordinates. At the equator, 0.00001 degrees is approximately 1.1m in length, so we can divide the world into approximately 1m^2 sections. \",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Then each position data’s coordinates are truncated to 5 decimal places, and stored under the corresponding “section”. When there are position data for multiple devices stored within the same section, we can loosely say that they are within 1 meter proximity of each other.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Taking the accuracy radius and the contact proximity parameter into account, the system can increase the contact zone around the original coordinates by expanding surrounding grid sections, 1 meter per section each direction, and when two contact zones overlap, by the same logic, the overlapping parts would be represented by having position data from two devices sharing the same grid sections.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Illustration showing how a device is within 5 meter proximity of another device, by marking their positions on a grid map, where the red dots represent the infected device’s position with a 2 meter accuracy, the yellow dots (should fill up the rest of the space within the red circle) represent a 5 meter radius proximity around the infected but are still duplicates of the red dots, and the blue dots represent the position a nearby device with a 3 meter accuracy that is potentially within the infected device’s exposure zone.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"embedded-asset-block\",\"data\":{\"target\":{\"sys\":{\"id\":\"5KJZMO7rnMm6MQrurj5c4G\",\"type\":\"Link\",\"linkType\":\"Asset\"}}},\"content\":[]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"With this understanding, when position data for a device is ingested, the same position data can be duplicated to cover the whole accuracy and proximity radius and persisted in the data store indexed by the respective coordinates truncated to the 5th decimal place. And a “contact event” is derived when two position data occupy the same position. \",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"While the above approach illustrates the idea, it will blindly apply the 5 meter proximity radius to all position data ingested and can create false positives when two devices are in fact 10 meters apart. Therefore the proximity value should be decreased to 2.5 meters (or 3m, since the grid unit is 1m) so that the max proximity is still 5 meters.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"embedded-asset-block\",\"data\":{\"target\":{\"sys\":{\"id\":\"7ES8VZusPm4uN28ee7swPu\",\"type\":\"Link\",\"linkType\":\"Asset\"}}},\"content\":[]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"So far with this approach we are able to deduce the spatial side of the problem, ie. given the position of a device of someone reported as infected (the red and pink dots), what other devices are nearby in proximity (dots also shared by other devices).\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Problem:\",\"marks\":[{\"type\":\"bold\"}],\"data\":{}}]},{\"nodeType\":\"ordered-list\",\"data\":{},\"content\":[{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Sheer number of dots to update for each position event.\",\"marks\":[],\"data\":{}}]}]},{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"The distance of 0.00001 degrees decreases in latitude the further we're away from the equator (at 45 degrees N it is 0.78m), so accounting for that will add more complexity.\",\"marks\":[],\"data\":{}}]}]},{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Rounding 2.5m to 3m for proximity effectively makes it a 6m proximity.\",\"marks\":[],\"data\":{}}]}]}]},{\"nodeType\":\"heading-3\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"MongoDB/Postgres (PostGIS) Geospatial Queries\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Another potential way is to use MongoDB’s or Postgres with the PostGIS extension geospatial indexed queries. The position events could be stored as circles/polygons and then the DB could be queried to return other polygons that are within the proximity radius.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Problem:\",\"marks\":[{\"type\":\"bold\"}],\"data\":{}},{\"nodeType\":\"text\",\"value\":\" It is a lot of coordinates to store. For a multi tenant system with millions of events per tenant per day, long term scalability is a concern.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"heading-3\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Pre-calculated Bounding Box and Filter\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"embedded-asset-block\",\"data\":{\"target\":{\"sys\":{\"id\":\"3g195amOQf2CXMyDZqT0Uc\",\"type\":\"Link\",\"linkType\":\"Asset\"}}},\"content\":[]},{\"nodeType\":\"blockquote\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Picture illustrating the approach described, where the red dot is the position event in question, orange dots are the position events within proximity and the green are ones queried but out of proximity.\",\"marks\":[],\"data\":{}}]}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"We know that the maximum distance when two points can be considered “in proximity” is\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Therefore if we can query for all the points within this (acc + 9m) radius it should guarantee to contain at least all the points in proximity. From there all we need to do is filter out the ones that are actually not in proximity, ie. after subtracting accuracy values from both points, the distance is greater than 5m (\",\"marks\":[],\"data\":{}},{\"nodeType\":\"text\",\"value\":\"distance - acc1 - acc2 > 5m\",\"marks\":[{\"type\":\"bold\"}],\"data\":{}},{\"nodeType\":\"text\",\"value\":\").\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"The advantage of this is that we only need to store points, and two options to query this data.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"ordered-list\",\"data\":{},\"content\":[{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Mongo/Postgres geospatial queries: Less data, computation is done on DB side, we’re unsure about performance when data scales up in a multi-tenant centralized system. But for a system that contains only a single tenant this should be sufficient.\",\"marks\":[],\"data\":{}}]}]},{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Cassandra/Scylla: It doesn’t support geospatial queries, so we can’t really query for everything within a circular radius, but querying for a bounding box is a lot simpler, it would require us pre-calculating the min and max lat/lon values of the bounding box and then it’s simply a matter of querying for everything within a lat/lon range. There will likely be more points returned due to a larger search area, but the extra computation in calculating distance is likely going to be insignificant.\\nDue to the nature of the data (volume and throughput), Cassandra or Scylla DB makes a lot of sense since we’d only be inserting and reading, not updating.\",\"marks\":[],\"data\":{}}]}]}]},{\"nodeType\":\"heading-2\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Contact Event Aggregation\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"To factor in the temporal aspect, since it is unlikely that two position data will be created at the exact same time, there will also need to be a time threshold for contact. To simplify the problem, the timestamp of each position data can be truncated down to the minute and all data within the minute is then bucketed to the same minute bucket.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Combined with one of the above approaches to associate spatial relationships, we can identify a contact in a particular minute time slice. A naive algorithm to aggregate each one minute contact event is to store each contact event at time T, starting with duration of 1 minute, but before inserting, check if there’s a contact event that ends with T-1min as well as event starting at T+1min, and if so, merge the events, increment the duration and delete the obsolete events.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Risks and downsides:\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"ordered-list\",\"data\":{},\"content\":[{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"To alter the proximity value from 5m would require all position data to be reprocessed.\",\"marks\":[],\"data\":{}}]}]},{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"This assumes position data is updated at least once a minute.\",\"marks\":[],\"data\":{}}]}]},{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Accuracy of the position data - we will need to discard data with low accuracy (high accuracy radius), and this may result in data disjointed in time ( {\",\"marks\":[],\"data\":{}},{\"nodeType\":\"text\",\"value\":\"<\",\"marks\":[{\"type\":\"code\"},{\"type\":\"bold\"}],\"data\":{}},{\"nodeType\":\"text\",\"value\":\"}5 minute sequence)\",\"marks\":[],\"data\":{}}]}]},{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Proximity zones do not consider walls.\",\"marks\":[],\"data\":{}}]}]}]},{\"nodeType\":\"heading-2\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Architecture diagrams\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"heading-3\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Initial backend architecture\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Initially for the backend, everything will be done within one service.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"ordered-list\",\"data\":{},\"content\":[{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"The device sends position updates to the ingestion API in batches by HTTP POST.\",\"marks\":[],\"data\":{}}]}]},{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Server discards position events with accuracy larger than 4 meters, inserts position events to the database. There can only be one position event per device per minute, anything more than one will also be discarded.\",\"marks\":[],\"data\":{}}]}]},{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"The server queries the database for position events from other devices within that minute with latlon within a bounding box that surrounds a proximity radius (accuracy+5m proximity+4m max accuracy) defined by the bottom left (lower limit) and upper right (upper limit) coordinates.\",\"marks\":[],\"data\":{}}]}]},{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Filter the position events that are too far.\",\"marks\":[],\"data\":{}}]}]},{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"For each “position event” to “position event within proximity” pair, create a minute aggregate, and create a contact event of one minute. Look up immediate contact events prior and after and merge and extend as necessary.\",\"marks\":[],\"data\":{}}]}]},{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"The dashboard backend retrieves the contact events with duration at least 5 minutes.\",\"marks\":[],\"data\":{}}]}]}]},{\"nodeType\":\"embedded-asset-block\",\"data\":{\"target\":{\"sys\":{\"id\":\"4SulsjoBXYrVnNmaJiHP1o\",\"type\":\"Link\",\"linkType\":\"Asset\"}}},\"content\":[]},{\"nodeType\":\"heading-3\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Longer term\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"embedded-asset-block\",\"data\":{\"target\":{\"sys\":{\"id\":\"5XHpoIh8YzdjRq0hccudwT\",\"type\":\"Link\",\"linkType\":\"Asset\"}}},\"content\":[]},{\"nodeType\":\"unordered-list\",\"data\":{},\"content\":[{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"gRPC is more efficient than traditional HTTP methods\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"unordered-list\",\"data\":{},\"content\":[{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"HTTP/2: one long lived connection between client and server\",\"marks\":[],\"data\":{}}]}]},{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Protocol buffers (binary data) is lightweight compared to JSON\",\"marks\":[],\"data\":{}}]}]}]}]},{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Kafka for position events for asynchronous throughput.\",\"marks\":[],\"data\":{}}]}]},{\"nodeType\":\"list-item\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Kafka for minute aggregates. Concurrent inserts of contact events between two devices could lead to single minute events that are unmerged. This can be alleviated by making sure only the events for any two devices are handled by only one process with proper partitioning in kafka.\",\"marks\":[],\"data\":{}}]}]}]},{\"nodeType\":\"blockquote\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Due to time constraint we actually only used MongoDB for the short term solution due to ease of getting started. And due to imitation of data partitioning in the server code for proof of concept, this means the server code can only be run in a single instance.\",\"marks\":[],\"data\":{}}]}]},{\"nodeType\":\"heading-2\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"Load testing\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"In order to test our architecture for performance and scale, we developed a fake data generator that can simulate devices continuously send data to the ingest api for processing. We simulated 1000 devices sending their position every 15 seconds to load test our initial architecture. We were able to process the data in realtime without any issues. We then built a simple visualizer that inspects the results of the processed data.\",\"marks\":[],\"data\":{}}]},{\"nodeType\":\"embedded-asset-block\",\"data\":{\"target\":{\"sys\":{\"id\":\"4t1sbIjYv1zPd6x716M5Cr\",\"type\":\"Link\",\"linkType\":\"Asset\"}}},\"content\":[]},{\"nodeType\":\"blockquote\",\"data\":{},\"content\":[{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"The circles represent a position sent by the device. The position is never a dot since every position data returned by mobile devices has an accuracy. The size of the circle represents the accuracy of the position. A red line between two circles indicates that the system has determined a contact event has happened.\",\"marks\":[],\"data\":{}}]}]},{\"nodeType\":\"paragraph\",\"data\":{},\"content\":[{\"nodeType\":\"text\",\"value\":\"\",\"marks\":[],\"data\":{}}]}]}"},"articleType":"Dev Content","image":{"fluid":{"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAMAAACejr5sAAABN2lDQ1BpY2MAAHjaY2BgEkgsKMhhYWBgyM0rKQpyd1KIiIxSYH/GwMYgy8DBYMGgnphcXOAYEOADVMIAo1HBt2sMjCD6si7IrLmRF9qCNlu/FeXntn9foJvCgB9wpaQWJwPpP0CcnFxQVMLAwJgAZCuXlxSA2C1AtkgR0FFA9gwQOx3CXgNiJ0HYB8BqQoKcgewrQLZAckYi0F7GJ0C2ThKSeDoSG2ovCHAambgbhRu6mzBQGZSkVpSAaOf8gsqizPSMEgVHYAilKnjmJevpKBgZGBkxMIDCG6L68w1wODKKcSDEapQZGCxrGRiYUxFiQdsZGHaKMTDwFiPE1IHhx5vDwHAopCCxKBHuAMZvLMVpxkYQNjdQH+u0//8/hzMwsGsyMPy9/v//7+3///9dBjT/FgPDgW8AsCNdY3Mb3eoAAAKgUExURQ4nOThHXXVwfaKSmbenqryjnsSspsOuqLKemo2DhlFaZxIlMxAnOAseLQ8hMAodLAkaKAgbKggbKQ4jMik4R09RXGddYY12T6OJT6KKUXpnX2BXWkVKUjU/OSEuJiAxOhIkMCQxPR0tORotOw4fLQsbJw4cJAsfLyI5Syo8TSg0PzA3LlZUJUBDKCgyMQ8lNwgnOwoeLAsiMA8fJxYpMRsrNhEhLQ0gLw0eKg8kMgsbKAshNgshMQwdKBwqJCIvJBooJR0qJAscKAcdLQceMAcdLw0eKRMmMRspNBIjLwwdLA0eKAwaJQoaKBIuQBIuPw8gJyo4KhopJggcKwYeMAobKCE2RhooKSUvHyQuJSErIBQhJAwdKg4gLg8dJQokNQkfLgkdLBgyRQ4mNgsfLRUkJSIvIxkoJhspJAkbKQkdKwcfMQ0gLiAqJCIrIBQgIwsaJggaKQkaJwYdLgggLgceLwgbKwobKRcsPBQqOSAsJR0rIyo1IyUvKykzKRYiJg4cJgsaJwsfKwofKw0eJhIjJwocKAYcLQYdLwcfMAocKg8fLBIkKxsrKyg1KyczNCQyNRUkKhMgJRMjKw4dJhYmJxMkJwkcKwccLAsfLg4lMxEnNRAlMhEkLh0uLCAuLhwrLx0nIRYjJAwbJwUeMAwdJw4fKBEhJhkpJxIkKRQpOBYsOxMjKBcnKBQjLg0cKQoZJgkZJwccLQocKRQjJRMiJQ0fKQsgLQwhLwoaKQkZLA0dKhwmJSAqIRolIxMhJhMgJA0cJQwgLg0mNg8hKgYfMRAhLyQ3Rig5RhYlMBIiLg4gLB0yQQ8iMAscKQweKxIiKA8mNBIsPREoOCA2RgsdKgsdKxMhJBwoIxYlJxcpNh0rNhsoJxclJxknKQwdK4BpIgwAAAAJcEhZcwAAFxEAABcRAcom8z8AAAAHdElNRQfmCR0THDR6CQvNAAAAg0lEQVQY02NkYEQH/5kYhRkxAYsIAcF/zDBBiPYPgntNBBDS8Vi1M1wwhGk8Zg2mdjI2QWWRncYi/EGQca8L0G1wMOsF00P5/XxB/IICQLCYn5+Pjy/ftIdx03avhkbGT/xIurtZWCIZ/wm0cxT5GjbDBJtZpIAkfycj414OqNBbkYkAGdkeRGJq4bgAAAASdEVYdGV4aWY6RXhpZk9mZnNldAAyNlMbomUAAAAZdEVYdGV4aWY6UGl4ZWxYRGltZW5zaW9uADU3NjAb6dbQAAAAGXRFWHRleGlmOlBpeGVsWURpbWVuc2lvbgAzODQwvhN3WwAAACh0RVh0aWNjOmNvcHlyaWdodABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAyMuS0v5wAAAAYdEVYdGljYzpkZXNjcmlwdGlvbgAyNEcyVzFHNKmtL4wAAAAASUVORK5CYII=","aspectRatio":1.5,"src":"//images.ctfassets.net/wdjnw2prxlw8/1giaUlq0r1Bd9jtYEz9u4I/6c422ad9fb1a15d2726f46fcf7090506/image.png?w=480&q=90","srcSet":"//images.ctfassets.net/wdjnw2prxlw8/1giaUlq0r1Bd9jtYEz9u4I/6c422ad9fb1a15d2726f46fcf7090506/image.png?w=120&h=80&q=90 120w,\n//images.ctfassets.net/wdjnw2prxlw8/1giaUlq0r1Bd9jtYEz9u4I/6c422ad9fb1a15d2726f46fcf7090506/image.png?w=240&h=160&q=90 240w,\n//images.ctfassets.net/wdjnw2prxlw8/1giaUlq0r1Bd9jtYEz9u4I/6c422ad9fb1a15d2726f46fcf7090506/image.png?w=480&h=320&q=90 480w,\n//images.ctfassets.net/wdjnw2prxlw8/1giaUlq0r1Bd9jtYEz9u4I/6c422ad9fb1a15d2726f46fcf7090506/image.png?w=720&h=480&q=90 720w,\n//images.ctfassets.net/wdjnw2prxlw8/1giaUlq0r1Bd9jtYEz9u4I/6c422ad9fb1a15d2726f46fcf7090506/image.png?w=960&h=640&q=90 960w,\n//images.ctfassets.net/wdjnw2prxlw8/1giaUlq0r1Bd9jtYEz9u4I/6c422ad9fb1a15d2726f46fcf7090506/image.png?w=1440&h=960&q=90 1440w","srcWebp":"//images.ctfassets.net/wdjnw2prxlw8/1giaUlq0r1Bd9jtYEz9u4I/6c422ad9fb1a15d2726f46fcf7090506/image.png?w=480&q=90&fm=webp","srcSetWebp":"//images.ctfassets.net/wdjnw2prxlw8/1giaUlq0r1Bd9jtYEz9u4I/6c422ad9fb1a15d2726f46fcf7090506/image.png?w=120&h=80&q=90&fm=webp 120w,\n//images.ctfassets.net/wdjnw2prxlw8/1giaUlq0r1Bd9jtYEz9u4I/6c422ad9fb1a15d2726f46fcf7090506/image.png?w=240&h=160&q=90&fm=webp 240w,\n//images.ctfassets.net/wdjnw2prxlw8/1giaUlq0r1Bd9jtYEz9u4I/6c422ad9fb1a15d2726f46fcf7090506/image.png?w=480&h=320&q=90&fm=webp 480w,\n//images.ctfassets.net/wdjnw2prxlw8/1giaUlq0r1Bd9jtYEz9u4I/6c422ad9fb1a15d2726f46fcf7090506/image.png?w=720&h=480&q=90&fm=webp 720w,\n//images.ctfassets.net/wdjnw2prxlw8/1giaUlq0r1Bd9jtYEz9u4I/6c422ad9fb1a15d2726f46fcf7090506/image.png?w=960&h=640&q=90&fm=webp 960w,\n//images.ctfassets.net/wdjnw2prxlw8/1giaUlq0r1Bd9jtYEz9u4I/6c422ad9fb1a15d2726f46fcf7090506/image.png?w=1440&h=960&q=90&fm=webp 1440w","sizes":"(max-width: 480px) 100vw, 480px"}}}}],"totalCount":8}},"pageContext":{"name":"Developers","slug":"developers","limit":15,"skip":0,"numPages":1,"currentPage":1}},
    "staticQueryHashes": ["1252674921","3226752585","3508205345","764694655"]}