Streaming is available in most browsers,
and in the WWDC app.
Adding Indoor Maps to your App and Website
The Indoor Maps Program enables organizations with large public or private spaces to deliver user experiences that provide precise indoor location information and present stunning indoor maps. Discover the overall process in the indoor map enablement workflow then take deep dive into the technical details on how MapKit and MapKit JS use powerful APIs and geo-standards to rapidly integrate indoor maps into your app and website.
- Apple Developer: MapKit JS
- Displaying an Indoor Map
- Displaying Indoor Maps with MapKit JS
- Presentation Slides (PDF)
Hello and welcome. I am Stephane, an engineer in the maps team, and together with my colleague, Mithilesh, we are going to talk about adding indoor maps to your app and websites.
In iOS 11, we introduced indoor maps as a part of Apple maps. We provide beautiful and detailed floor plans for venues such as airports and malls. Indoor maps is a great way for you to view, search, and find your way inside of indoor spaces.
Using the level picker, you can switch between floors, and each of these venues have indoor location similar to GPS or better.
Today, we will talk about how you can display indoor maps inside your own applications along with indoor positioning.
We'll start by talking about the Indoor Mapping Data Format. Then, we'll introduce some of the tools available to you as a part of the Indoor Maps Program. We'll then show you how you can display indoor maps inside your iOS and web applications. Let's get started. IMDF, which stands for the Indoor Mapping Data Format, is a specification for modeling indoor spaces. But before going into more details, let's step back a bit and take a closer look at what an indoor map is in general terms. An indoor map, like your regular map, is comprised of many layers, and each of these layers may contain several features. Let's break this map, for instance. The base of an indoor map is the building footprint. Then we get a level. On that level, we can find units, such as rooms and walkways. Units have doorways, and we can also have kiosks that you often see in malls. We have labels for rooms and kiosks, such as the name of a business occupying a room, and also icons that mark the location of amnities of interest like elevators or restrooms. And finally, there is also a virtual boundary enclosing an area with multiple units.
Okay, so these are some of the layers that make an indoor map. However, in order to be able to accurately describe-- in order to be able to create and display such a map, we need a way to accurately describe its contents. That's where IMDF comes into play. IMDF is a specification for modeling indoor spaces. It is a 2D GeoJSON based data format that's not only easy to create and understand but also easy for applications to use. An IMDF archive is a set of JSON files.
More specifically, it consists of a manifest JSON file and several GeoJSON files, each containing a collection of features of the given IMDF feature type. An IMDF feature is a regular GeoJSON feature. It has a type feature, may have a geometry, and has a set of properties.
The IMDF specification also requires an ID to present, which is a UUID and an additional field called feature type, which represents the indoor concept being modeled. Let's take a look at some of the main feature types of IMDF.
Levels model the location and physical extent of floor areas. A level has a full name, like parking level one, a short name like P1, and a reference to the building features it belongs to. It also has an ordinal, which represents the level's position within the total range of floors in a building.
Ground levels have an ordinal equal to 0, subterranean levels have negative ordinals, and, of course, above-ground levels have positive ordinals.
A level is paved with units. A unit models the location and extent of a space, like a room, a walkway, a stairwell, or an elevator.
Openings model entrances such as door. They may also define interesting properties like the type of accessibility or the kind of access control systems. Kiosks model pieces of furniture that may be used to provide services or distribute products, like information desks or vending machines. We can also add businesses to our map thanks to the occupant features. An occupant provides information about a business like the name, the phone number, and opening hours. They don't have a geometry, but we can get their display point and address through their associated anchor. Anchors represent a curated point used as a peripheral display location in a unit. They may also have a reference to an address. Anchors play a key role in IMDF since other features like occupant, unit, or kiosk may be linked to addresses through them. We may also want to add amenities. Amenity features can model permanent equipment or other convenience of interest like a photo booth, and ATM, or an exhibit in that case. Section features may be used to describe the extent of an area with a particular theme on the level like a food court in a mall or a post-security area in an airport. Sections do not need to be materialized by a physical boundary.
Buildings describe physical building structures. They may have a name and also refer to an address. However, they don't have a geometry, but defer job of describing their extent to another feature called footprint.
And there are three types of footprints. Aerial footprints capture the maximum extent of the levels above ground. Ground footprints capture the maximum extent of the ground levels, and subterranean footprints capture the maximum extent of the floors below ground.
Okay. So this was just a quick overview of the main features of IMDF. Thanks to IMDF, we can describe indoor spaces data in a formal yet flexible way. But, of course the specification covers a lot more. You can check it out at register.apple.com. Apple works with many of the top platforms' providers for geospatial tooling that support IMDF. You can work directly with them to create an IMDF archive for your venue.
You can use that archive right away and display it in your apps as we will demonstrate later in this session. But what if you also want to enable indoor positioning? Well, that's where the indoor maps program comes in. By participating in the indoor maps program, you can take advantage of some of the tools that Apple provides to visualize and validate your data. Even more interesting, you can add indoor location capability or the blue dot to your venue. And optionally, you can also allow Apple to display your venue inside of Apple Maps. You can sign up for the indoor maps program at register.Apple.com/indoor. Signing up is very straightforward and easy. You just need to provide some basic information about yourself and your organization such as the name, the location, and address. Once Apple has reviewed and approved your application, you can start working with your geospatial tool provider to create an IMDF archive. And now that you have it, you are ready to validate it with the IMDF Sandbox. The IMDF Sandbox is a tool that allows you to visualize and validate your IMDF data. Most of the issues it reports can be addressed right away with a few clicks. Some errors might require more work, and, in that case, you may want to take your data back to the mapmaker to fix them. When the IMDF Sandbox reports no errors, you may submit your data for more exhausting validation tests. If no issues are detected, you can proceed to the next step, which is enabling indoor positioning for your venue with the indoor survey app. The indoor survey app is your companion to enable indoor positioning on iOS. You survey a place by collecting radiofrequency fingerprints of your venue. Surveying relies on the fact that the radiofrequency patterns emitted by fixed WiFi access points inside a building are unique depending on your location. Once surveying is complete, the survey data is uploaded to Apple servers and processed. And, if no issues are detected, your venue is made live for indoor location. You can use a survey app to test the accuracy of indoor location afterwards. Performing a survey is very easy and intuitive. We have some guidelines in place so that you can get the best results out of it.
Alright. So, you have created your IMDF archive and validated it, and you may also have enabled indoor positioning. The next step is to display it in your app or website. I'll now hand over to Mithilesh, who will show you how you can display an indoor map inside an iOS application. Thank you, Stephane.
Hello everyone. My name is Mithilesh, and I'm an engineer on the maps team. I'm here to show you how you can use IMDF data to display an indoor map in your iOS application. We'll build a simple app for visitors of a dinosaur museum. We call this app Dinoseum. The app displays a map of a museum with different exhibits, restaurants, shops, and more. The various areas of the museum are marked with descriptive labels and icons that the user can tap to display details.
On the top right corner, we have a level picker to switch between levels. The app also takes advantage of indoor positioning to display the user's location inside the museum. So, what does it take to build this app? Displaying a basic indoor map can be done in three steps.
In step one, we'll add the IMDF files to our project and create model classes for each of the feature types. In step two, we will decode the IMDF files and create instances of these model classes. We will then relate them to create a graph of feature objects.
Venue consists of buildings. Buildings have footprint and levels. Levels have units and openings and so on. In the third step, we will retrieve the geometry from the decoded feature objects and render them to create an indoor map.
Now, let's take a quick look at some of the code that we'll write.
For step one, we'll create an abstract class called feature.
Every IMDF feature must have a unique identifier. It must also have a set of properties that describe the feature. And it may have geometry, which is an array of MKShape objects.
Using this as the base class, we'll write concrete implementations of feature class such as Unit.
For decoding an IMDF file, which is also a GeoJSON file, we'll use the new MKGeoJSONDecoder and call decode on the data. We'll look into the details of decoding in a short while. During step three, we will use addOverlays and addAnnotations APIs of the MapKit framework to draw polygons, lines, and point geometry on the map. And for every overlay that is added to the map, we get a callback to MKMapViewDelegate method mapView render for overlay.
And for every annotation, we get a callback to mapView view for annotation. In these delegate methods, we'll have the opportunity to customize the appearance of the indoor map elements. And with that, let's start building our dinosaur museum app.
We'll start with a very basic app. This is a single view application based on MKMapView.
The first thing I want to do is to make sure I have added my IMDF files to the project. And here they are. In your application, you could choose to either bundle these files with your app or download them from your servers.
Let's take a look at one of these files.
This is for the Unit feature type. Our goal is to decode and render the contents of this file.
I'll now write the abstract class called Feature.
This has the three properties we saw earlier. Identifier, properties, and geometry. Our class is missing an initializer, so let's add one.
Initialization is straightforward with the exception of properties. MKGeoJSON feature exposes property as opaque data, and we must decode it before we can use this.
We use the JSON decoder class to decode our data and convert it into adjacent object. And now, I can write concrete implementations of Feature class. We'll do that for the unit type.
Let's set the base class to be Feature.
To describe the unit feature type, we need to define some properties.
It consists of category, which is used to differentiate between different kinds of unit and level ID, which is the identifier of the level this unit belongs to. Units have occupants and amenities. So let's add them here. And that's all we needed to do for the unit feature class. Similar to unit, we can create model classes for venue and level and all the other feature types that we wish to decode. I've already implemented them and added them to the project. You can check them out by downloading the sample code provided for this session. Okay, now we can move onto step two, which is to decode the IMDF data. We'll do that in a class called IMDFDecoder. Let's add a property of type MKGeoJSONDecoder. This is the new class available in the MapKit framework that you can use to decode any GeoJSON data.
I'll now write a method called decodeFeatures that decodes a single IMDF file. In this method, we first read the contents of the IMDF file from disk and by using the instance of MKGeoJSONDecoder, we all the code on the data. We get back an array of MKGeoJSONFeature objects, which we can use to initialize our model classes. I'll now write a method called decode that decodes an IMDF archive or a collection of IMDF files.
Let's create the instances of the feature objects that we wish to decode such as venues and levels and units, and we use the decodeFeatures method I wrote earlier in passing in the type of feature I wish to decode.
To make it easier to render the correct subset of features, we need to create associations between these feature objects. For example, venue contains levels. So let's relate them. We do that after grouping the levels by the ordinal.
Levels contain units and openings, so let's make that association.
Both units and openings have a property called levelID. We group the units and levels by the levelID, and then iterate through all the levels of this venue and find the units that are contained in this level and relate them. And the same thing for the openings.
Units contain amenities and occupants, so let's create that association.
We iterate through all the amenities, find the unit it belongs to, and make the association. In just the same way, we can relate occupants to units as well.
But there's one exception for occupants. Remember that we use occupants to display a business' name on the map, but they do not have geometry of their own. Instead, they rely on the anchor object to get the display point.
Okay. So, now we have a decode method that decodes and IMDF archive and gives us a fully decoded and configured venue object with references to all the feature types that we wish to render. So now we can move onto step three, which is to render the IMDF data on a map. We'll do that in our main view controller class called IndoorMapViewController.
Let's call the decode method that we defined in step two, and we get back a venue object, and now we can add features on the map as overlays and annotations.
And to do that, I'll write a method called showFeaturesForOrdinal where ordinal is an integer representing the level's position within total range of floors in the building. In this method, we first remove all the overlays and annotations that belong to a previous level and then aggregate the subset of features that we want to display for the current level and retrieve the geometry from those feature objects and add them to the map as overlays and annotations. Let's call this method from our viewDidLoad.
We set the initial level to be the one with ordinal one. We've already implemented the MKMapViewDelegate method, mapView, render for overlay, where we create instances of MKOverlayRenderer for every GeoJSON geometry type including the new MKMultipolygonRenderer and the new MKMultipolylineRenderer for much more efficient rendering of polygons and lines. Let's run this app and see if we have an indoor map.
Okay. We have a very basic map of a museum.
Let me zoom in a bit. In this map, the units are shown as polygons.
Amenities such as this restroom is displayed as an annotation, and occupants such as our Jurassic Table Restaurant is also displayed as an annotation. Our museum is a multilevel building, so it needs a level picker. Let's add one. I've already added a level picker to the story board. It's a UIStackView based level picker. I'll make it visible. And to receive the level picker related update messages, we have implemented a level picker delegate, which has a method called selected level did change. This method is called every time the user taps to make a selection in the level picker.
Let's handle the level change event here.
When the level changes, we use showFeaturesForOrdinal to update the map with features for the selected level. Let's run and make sure we have a level picker. Okay. I can now tap the items in the level picker and see every single level of this museum.
Let's pause for a moment here and recap what we have done.
First, we created model classes for all the IMDF feature types.
Then we decoded the IMDF data using MKGeoJSONDecoder and created association between the feature types. And finally, we rendered IMDF data on a map view using addOverlays and addAnnotation APIs of the MapKit framework. You might have already noticed that our current map lacks any styling, and the annotations don't look very interesting. You can significantly improve the indoor map experience and functionality with two additional steps. First, by applying style to your indoor map elements. Styling your map is critical to how it is perceived and used. Use distinct colors and icons that match your app's team or your company's brand. And in the final step, we can make your app more useful by showing the user's current location.
Remember that you can take advantage of accurate indoor location on iOS by participating in the indoor maps program and having your venue surveyed using the Indoor Survey app. Luckily, we had a team survey our dinosaur museum, so we can go ahead and add location or the blue dot to the map. Let's take a look at some of the code that we'll write to help with styling. We'll define a protocol called StylableFeature, which has a property and two methods. Every stylable feature must have geometry. And to apply styles to overlays, we will implement the configure overlay renderer method.
Similarly, to apply styles to the annotations, we'll implement the configure annotation view method. I'll switch to Xcode and show you how this can be done.
To apply the styles, we'll take advantage of the asset catalogue where I have added colors very specific to the indoor map we are using here. So, to apply these colors, I'll go to a file called Styles.swift where I have defined the StylableFeature protocol.
Now, let's say we want to apply some styling to the amenity features.
We can do that by extending the Amenity class to adopt the StylableFeature protocol. And because amenities have point geometry, we will implement the configure annotation view method, where we set the annotation view's background color to a default fill color from the asset catalogue. We also take this opportunity to set the display priority of the annotation to a default low. In a similar way, we can extend the unit class to adopt the StylableFeature protocol to apply styling to units.
But for units, we want to apply unique colors for every category of units. So, to do that, I will define and enumeration type called StylableCategory, and the values in this are the category names that we wish to style. And because units have polygon geometry, we will implement the configure overlay renderer method. And in this method, we handle the different values of the unit category and apply different fill colors to each one of them. I'll now switch to the main view controller class, and in my mapView renderer for overlay method, instead of using the same stroke and fill color for every polygon, we will now call configure overlay renderer method to apply feature-specific styling. In a similar way, for annotations, I will call configure annotation view and apply feature-specific styles to annotations. We want to render occupants as a simple dot and a label and the amenities with just gray dots. To do that, I have implemented custom annotation views and included them in the project. Here, we're calling the configure annotation view and set the feature-specific style to that annotation view. So, let's run this app and see if our map looks any different. Okay.
This definitely looks better than what we had before. Now, we can see the different units in different colors, and the gray dots are our amenity points. And the occupants such as the Jurassic Table Restaurant is displayed with a dot and a label. But something is not right here. This area on the map, I believe, is a unit of category walkway, and its color is not so different from the other units around it. Let's see if we can fix that.
To do that, I'll go to the Style.swift file and here let's add a value of type, for the walkway category. And we must handle this in a configure overlay renderer method.
Where we are setting the color, the fill color of the walkway to a color called walkway fill, which should be there in our asset catalogue. Let's make sure. Okay. We have a white color set for that. I'll run the app and see if we have fixed that problem. Okay. Cool. This definitely looks better. Simply by applying a distinct color to the walkways, the appearance of our map has improved significantly. Let's make one more enhancement here. The gray dots for the amenities are good, but I think icons are better. And what's better than icons? Dinosaur icons. So, let's use them.
Our designer has given us some icons that we have included in our project. Let's add these to the map. I'll go to my Styles.swift file, and the dots we saw were for amenities, so let's make a small change here.
For convenience, we have set the icon's name to be the same as the name of the category for the amenity. So we look at the asset catalogue for that particular name. If we find an icon, we set it to the annotation view's image property, but if we don't, then we continue displaying the gray dots. Let's run the app and see.
Cool. Now we have some beautiful-looking icons on the map, and we can clearly see where the restrooms are and the escalator and the elevator and more importantly the exhibit called T-Rex.
One last thing. We know that we have indoor location available at this museum. So let's take advantage of that. Let's add the user's location to the map. To do so, I'll go to the map view and enable the user location from here. In my view controller, first I'll add a property of type CLLocationManager and then use this property to request the user's permission to use location services while the app is in the foreground. And every time we get a location update, we get a callback to MKMapViewDelegate method, mapView did update user location. So let's handle the location updates. First, we check to see if the user is inside the venue. If the user is inside, there's no need to update the map.
If the user is outside, there's no need to update the map, but if the user is inside, then we use the CLLocation property on MKUserLocation to get the user's coordinates. CLLocation not only provides the user's longitude and latitude but also the floor information if your venue has been surveyed and the user is inside that location. So, we'll use the floor information from the CLLocation object and update our map using the showFeaturesForOrdinal to display the user's location. Let's run the app one more time.
I'll hit allow, and here we have the user's indoor location. I must mention that if the user were to switch between levels, the map will update the user's current level. And that's all I have for you in this demo. I'll now hand over to Stephane, who will show you how to display and indoor map in a web app. All right. With MapKit JS we can achieve similar indoor maps look and feel on the web. I will demonstrate how to render our map with MapKit JS and bring the same indoor maps experience to all major browser implementations. The key MapKit JS method that we will use for this demo is import GeoJSON. ImportGeoJSON takes two parameters. The first one is the GeoJSON feature to convert and the second one is GeoJSON delegate object that allows us to customize how items are created. In this basic example, we have two delegates. GeoJSONDidError that allows us to react on errors and GeoJSON did complete that gives us an array of the items that were just created.
Then we show these items by giving them to the showItems method. We can also use addItems; the difference being that add items does not center the map on the added elements.
In order to style the overlays, like polygons and lines, we can use styleForOverlay. It takes a newly created overlay as a parameter and expects a style object to be returned. We can, of course, craft a brand new one, but we can also use the default one that is associated with the overlay through the style property and customize it.
We can also customize how points are rendered with item for point. ItemForPoint takes a coordinate of the point feature as a parameter and should return an item. Here, we return a marker annotation. We can, of course, set options like the title and the color. We can also set the display priority. When the zoom level is too low and that many annotations are present on the map, they may collide. There are multiple strategies to deal with this issue like annotation clustering or display priorities. MapView JS may use display priorities to decide what annotations it should hide first.
With importGeoJSON we had everything that needed to create our dinosaur map. So let's get started.
Alright. So we start off our project with a simple mapView. I'm running an HTTP server that serves our web assets and is responsible for generating the MapKit JS authorization token. To draw the dinosaur map on the browser, we need to load the IMDF files and create MapKit items for each feature they contain. That sounds like a difficult task, but it's really not thanks to the importGeoJSON method that we just saw and the fact that IMDF uses GeoJSON. In the iOS demo, you saw that we created a graph of features. In this demo, we will take a slightly different approach by filtering the features on demand. We will create a class called IMDF archive that will provide the essential methods to load, organize, and filter the features.
The static load method downloads all the files that we need from the server, then aggregates the features into an array and passes that array to the constructor, and then we create two objects, featureById and featuresByType. They will help us afterwards.
Now we can call this method to load our features, and we can use import GeoJSON to create MapKit items for each of them. Okay. And now what we have to do is to call showItems with these newly created map items. So let's do that.
Okay. Let's try. Okay.
There is a lot of geometries showing up on our map, and that's expected because we asked MapKit JS to render all the features in our venue. What we need to do is to retain only the feature types that we want and to filter them by level. So let's implement a few methods in our IMDFArchive class that returns only the features of a certain type on a given ordinal. We'll start with levels.
Okay. So we retain only the levels that have the right ordinal, and similarly we can do the same thing for units.
Okay. Same thing with the level ID.
And we can do also the same thing for amenities in units, openings on level, and amenities and anchors on level.
Let's pause for a moment here. The fact that IMDF files are plain old GeoJSON makes our job so easy. There is not complicated parsing or decoding logic. We only have to know what properties we expect depending on the feature type.
Okay, so now let's rewrite our rendering logic.
We will create a function called createItemsForOrdinal that will create the items for a given ordinal.
Now, what we need to do is to get the features for that ordinal. So let's start with levels, okay, and for each of these levels, we will get all the units and similarly amenities and openings.
Okay. So that's all the features that we are interested into right now. Okay. And now what we need to do is to show them. So, we call create items for ordinal with an ordinal, let's say ordinal 1, which would be second floor and add them to the map.
Alright. Let's write that. Okay. So, that's a lot better. Now we have a clean view over the structure of the second floor. Now, let's add a level picker. I already implemented a simple level picker that is just a basic HTML list. That's what I did with level names of our venue. You can check out the implementation details in the sample code that's linked to this session. The level picker will use our createItemsForOrdinal function to create items if needed. I already imported the script, so the only thing that I need to do here is to instantiate it and select the ordinal 1, so that the second floor shows up when the page is loaded. Let's try again.
Okay. So, now I can switch between floors. Okay, so now what we need to do is to add some styling. All units look the same, and we cannot even distinguish the opening LineStrings from the rest of the geometries, which gives us the false impression that all the units don't have any doors. That's a bit confusing. We can customize the style of polygons and lines by implementing the style for overlay function in the GeoJSON delegate object. So, let's start by drawing our openings in white.
Okay. So that's really simple. We just select the opening features and we set the stroke color to white.
And as we did in the iOS app, let's set a fill color to units depending on their category. In order to do that, let's create an object called unit style.
Okay. So that object will contain the styling properties for each unit category that we want to show. But we also need a default one if we don't have a style for some categories, so let's do that first. Okay, and now we can set the styles for the unit categories that we care about.
Okay. So, now we can go ahead and use that object to apply a style to unit overlays. Okay.
So what we do here is to get the proper unit style with the category and set fillOpacity, strokeColor, and fillColor to the style values or setting the default if there isn't.
That's actually a good example of how easy it can be to render IMDF maps. Most of the styling can be done by using a combination of the feature type and the category. Of course, we can also rely on all other properties as well. Okay. So, let's see the changes. Okay.
That's a lot better. Now we can see where the walkways are and what units our visitors can access to. Now, you may also have noticed all these red pins on the map. They are amenities. Remember, amenities have point geometries, and importGeoJSON renders point geometries with marker annotations by default.
So, what we would really like to have here is using the same icons as we did in the iOS app. For that, we will use MapKit JS ImageAnnotations. As we saw earlier, we can replace the default behavior of importGeoJSON by providing our own implementation of item for point. So let's do that.
Okay. So what we do here is inserting a name from the properties or fall back to the category if there isn't and use that name as a title of our annotation.
When the annotation is clicked, the title will appear in the callout. We also set a relatively low priority to all amenities so that we can set a higher one to the more important amenities, which are exhibits. We'll do that in a short while.
And finally, if there is, if we have a URL for our icon, we create an image annotation. But if there isn't, we create a dot annotation. I already implemented a simple dot annotation in a separate file, and I already imported it. So, we can use it here. Okay, so what we have to do here is to provide the right URL for the icons depending on the category. So, let's create an object for that called iconUrls. Okay.
So that's pretty straightforward. And now we can use that object to get the URL for our icon.
Okay. You may also have noticed that some of the entries here are really specific. They are not amenity categories. They are icons for our T-rex, sauropod, and bone hall exhibits.
So, we need to handle them a bit differently.
So what we do here is test the name, and if this is the right name, we set the right icon. Of course, we can also use the feature ID if we want here.
We also set a higher priority for all exhibits, and the highest of all for the T-rex exhibit. And last but not least, we also append the matching unicode character to the name of the exhibits. And yes, you heard that right, there is actually a unicode character for dinosaurs and T-rexes. Okay. So let's reload and see how it renders on a map.
Okay, so now that's a map that's worthy of Dinoseum! And look, between, if I zoom out very, very, very, very far, here is our last annotation standing, the dinosaur exhibit. Alright. So, of course what we can also do is to restrict the capacity of the users to zoom out too far. We can do that with a new setting from MapKit JS, which is cameraZoomRange. Okay. So let's try that again and see if we can zoom out now. No, we can't. That's great. Okay.
So the last feature type that we want to handle here is occupants. Remember, it's a bit more challenging though because occupants do not have a display point. But they are linked to anchors, and anchors have display points. So we can use the associated anchor to get the coordinate to place our MapKit annotation on the map.
So, let's implement a method in our ImdfArchive class that gathers all occupants on a certain ordinal along with their anchors.
Okay. So that method returns an array of objects containing the occupant and its associated anchor. So, now let's use it to render the occupants.
As you can see here, it's fairly similar to what we did for amenities with one exception. We don't provide the occupant by itself to the importGeoJSON method, but anchor because anchor has the geometry. We use the occupant name to set the title of the annotation. And I also set a specific class based on the category, so category-annotation, so that I can give a specific style to some categories of occupants.
I already provided a style in our CSS file. Okay. So let's reload one last time. Okay. Now we can see the Jurassic Table, which is our famous in-house restaurant. Okay, let's recap what we did. First, we downloaded the GeoJSON files from the server, extracted the features from them and organized them by type. Then, we implemented a few methods to get features of a certain type on a given ordinal. Then, we added a level picker so that our users are able to choose what level they want to see. And we provided a custom style to units and openings.
And finally, we created icons and custom annotations instead of these default marker annotations for amenities and occupants. With MapKit JS we were able to build a map that shares the same look and feel as its iOS counterpart. Now we have a beautiful map that we can embed in the Dinoseum website and provide the same user experience on all the major browser implementations. And on that note, I will hand over to Mithilesh to conclude.
I hope you like what you saw today. Before we end this session, I'd like to go through some of the best practices that apply to indoor maps.
Let's talk about styling.
You should design an indoor map that feels like a natural extension of your app. Use colors and icons that match your app's theme or your company's brand. Don't try to replicate the appearance of Apple Maps or another application. Instead, make sure the overlays, icons, and labels match the visual style of your app.
Select distinct styles to differentiate the features of your map.
Using specific color for categories such as elevator area makes it easy for people to spot them at a glance.
Use easily recognizable icons. Icons should not only be visually appealing but should also be effective in communicating their purpose.
You should adjust the map detail based on zoom level. Too much detail can cause the map to appear cluttered. Show large areas like rooms and buildings at all zoom levels, then progressively add more detail features and labels as the map is zoomed in.
You should include surrounding areas to provide context. Adjacent streets, playgrounds, and other locations can help people orient when they use your map.
And finally and very importantly, enable indoor user location on iOS to enhance the indoor map experience when your users visit the venue.
For more information, you may check out the related sessions, What's New in MapKit and MapKit JS and Introducing the Apple Maps Program. If you have any comments or questions, visit us at the lab right after this session. Thank you very much for coming out today, and I hope you have had a great conference. [ Applause ]
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.