README.md 34.9 KB
Newer Older
Kyle Day's avatar
Kyle Day committed
1
# ThinkGeo Mobile Maps
Ben Bai's avatar
Ben Bai committed
2

Kyle Day's avatar
Kyle Day committed
3
Welcome, we're glad you're here!  If you're new to ThinkGeo's Mobile Maps, we suggest that you start by taking a look at our quickstart guide below.  This will introduce you to getting a nice looking map up and running with some external data and styling.  After reviewing this, we strongly recommend that you check out our HowDoI samples for [Xamarin.Forms](https://gitlab.com/thinkgeo/public/thinkgeo-mobile-maps/-/tree/master/samples/xamarin-forms/HowDoISample). It's packed with examples covering nearly everything you can do with our Mobile Maps control.
4

Ryan Duan's avatar
Ryan Duan committed
5 6
## Repository Layout

7
`/docs`: An offline version the API documentation HTML pages.
Ryan Duan's avatar
Ryan Duan committed
8 9 10

`/samples`: A collection of feature by feature samples.

11
`/assets`: Any assets needed for the readme.md.
Ryan Duan's avatar
Ryan Duan committed
12 13

`README.md`: A quick start guide to show you how to quickly get up and running.
14

Kyle Day's avatar
Kyle Day committed
15
## Samples
16 17 18

We have a number of samples for both Android and iOS that show off ThinkGeo Mobile Maps' full capabilities. You can use these samples as a starting point for your own application, or simply reference them for how to use our controls using best practices.

19 20
- [iOS samples](https://gitlab.com/thinkgeo/public/thinkgeo-mobile-maps/-/tree/master/samples/ios)
- [Android samples](https://gitlab.com/thinkgeo/public/thinkgeo-mobile-maps/-/tree/master/samples/android)
21

Kyle Day's avatar
Kyle Day committed
22 23 24 25 26 27 28 29 30 31
## Quickstart Guides

- [Xamarin.Forms Quickstart Guide](#xamarinforms-quickstart-guide)
- [Android Quickstart Guide](#quick-start-display-a-simple-map-on-android)
- [iOS Quickstart Guide](#quick-start-display-a-simple-map-on-ios)

## Xamarin.Forms Quickstart Guide

This will introduce you to ThinkGeo Mobile Maps by getting a nice looking map up and running with ThinkGeo background map along with some external data on a Xamarin.Forms application. By the end of this guide, you should have a basic understanding of how to use the Mobile Maps controls.

Kyle Day's avatar
Kyle Day committed
32
![Simple Map](https://gitlab.com/thinkgeo/public/thinkgeo-mobile-maps/-/raw/master/assets/xamarinforms_quickstart_screenshot.png)
Kyle Day's avatar
Kyle Day committed
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197

### Step 1: Install Prerequisites

Visual Studio will help guide you to setup your XamarinForms environment for both iOS and Android. Refer to the [Xamarin for Visual Studio guide](https://docs.microsoft.com/en-us/xamarin/get-started/installation) for more info.

#### Android Prerequisites

- Xamarin
- The [Android SDK](https://docs.microsoft.com/en-us/xamarin/android/get-started/installation/android-sdk)
- An [Android Emulator](https://docs.microsoft.com/en-us/xamarin/android/get-started/installation/android-emulator/device-manager)

#### iOS Prerequisites

To develop on Mac, you need:

- XCode, which provides iOS emulator.
- A development IDE, it could be Visual Studio for Mac, Xamarin Studio or others.
- Xamarin.
- A provisioning profile is needed if you want to test on an iOS device.

To develop on Windows, you need:

- A development IDE, such as Visual Studio or Xamarin Studio
- Xamarin.
- A Mac machine with XCode installed and on the same network as your Windows machine.

### Step 2: Set Up a New Project

Create a new project and select the `Mobile App (Xamarin.Forms)` project template. Name your application `ThinkGeoMobileQuickstart` and select the Blank application project template.

Once created, there will be 3 projects in the solution:

- `ThinkGeoMobileQuickstart` - The is where our shared will live
- `ThinkGeoMobileQuickstart.Android` - Android specific code (no modifications will be needed to this project in this demo)
- `ThinkGeoMobileQuickstart.iOS` - iOS specific code (no modifications will be needed to this project in this demo)

Go ahead and run the application. By default, Visual Studio will set you up with an Android emulator if you do not already have one. If you wish to debug the application for iOS, set your starting project to `ThinkGeoMobileQuickstart.iOS` and Visual Studio will assist you to connect to your remote Mac machine.

### Step 3: Implement the code

Once your blank application is up and running, install the following NuGet packages for each project:

- `ThinkGeoMobileQuickstart`
  - `ThinkGeo.UI.XamarinForms`
- `ThinkGeoMobileQuickstart.Android`
  - `ThinkGeo.UI.XamarinForms.Android`
- `ThinkGeoMobileQuickstart.iOS`
  - `ThinkGeo.UI.XamarinForms.iOS`

Open the `MainPage.xaml` found in the `ThinkGeoMobileQuickstart` project and add the following XML namespace to the `ContentPage`:

```xml
<!-- MainPage.xaml -->
xmlns:ThinkGeo="clr-namespace:ThinkGeo.UI.XamarinForms;assembly=ThinkGeo.UI.XamarinForms"
```

Next, replace all elements in the `StackLayout` with a single `MapView`:

```xml
<!-- MainPage.xaml -->
<ThinkGeo:MapView x:Name="mapView" VerticalOptions="FillAndExpand"/>
```

Now that the `MapView` has been added to the MainPage, we need to setup the map on the code side. For that, you will need to add two usings to `MainPage.xaml.cs`:

```cs
// MainPage.xaml.cs
using ThinkGeo.Core;
using ThinkGeo.UI.XamarinForms;
```

Next, override the `ContentPage`'s `OnAppearing` method to setup the map:

```cs
// MainPage.xaml.cs
protected override void OnAppearing()
{
    base.OnAppearing();

    // Set the map's unit of measurement to meters(Spherical Mercator)
    mapView.MapUnit = GeographyUnit.Meter;

    // Add Cloud Maps as a background overlay. The keys provided below are just demo keys
    var thinkGeoCloudVectorMapsOverlay = new ThinkGeoCloudVectorMapsOverlay("9ap16imkD_V7fsvDW9I8r8ULxgAB50BX_BnafMEBcKg~", "vtVao9zAcOj00UlGcK7U-efLANfeJKzlPuDB9nw7Bp4K4UxU_PdRDg~~", ThinkGeoCloudVectorMapsMapType.Light);

    mapView.Overlays.Add(thinkGeoCloudVectorMapsOverlay);

    // Set the map extent
    mapView.CurrentExtent = new RectangleShape(-10000000, 10000000, 10000000, -10000000);
}
```

Build the project and make sure it builds successfully.

### Step 4: Activate a Free Evaluation License

If you try to run the application now, "A license is needed" exception will be thrown if a valid `.mapsuitelicense` file is not found. A free 60-day license can be created using the following steps:

#### Launching ThinkGeo Product Center

1. Run `ThinkGeo.ProductCenter.exe` to open the product center. This can be found in the `bin` folder of the `ThinkGeoMobileQuickstart.Android` or `ThinkGeoMobileQuickstart.iOS` project. (`ThinkGeo.ProductCenter.exe` can only be opened on Windows, there's a CLI version for Mac.)
1. Click on `Log In` in the upper-right corner, input the username/password to login or click `Create a new account` to create a free ThinkGeo account.

#### Activating and Creating an Android License

1. Click on the `ThinkGeo UI Mobile for Android` tab and activate an evaluation license.
1. To generate a runtime license for the sample app, you'll need to find the package name for your sample project. In Visual Studio, this can be found by right-clicking on the `ThinkGeoMobileQuickstart.Android` project in the solution explorer and navigating to `Properties -> Android Manifest -> Package Name`
1. Copy the `Package Name` to the `Runtime License` input box to the right of the Product Center and click `Create`. Save the newly created license to the `Assets` folder of the solution (`ThinkGeoMobileQuickstart.Android\Assets`).
1. Add the license to the project in the solution explorer by right-clicking on the `Assets` folder and selecting `Add -> Existing Item`.
1. Right-click on the license and select `Properties`. Ensure that the `Build Action` is set to `AndroidAsset`

#### Activating and Creating an iOS License

1. Click the `ThinkGeo UI Mobile for iOS` tile and then click on `Start Evaluation` (or `Activate License` if you already have purchased a full license). Now you can see a textbox with text placeholder `Bundle Identifer` on the right.
1. Get the project's bundle identifier in `info.plist`, copy and paste it to the 'bundle dentifier' textbox in product center.
1. Click 'Create' and save the license file (the file name would be `<bundle-identifer>.mapsuitelicense`) to the solution's root folder.
1. Add the license to the project in the solution explorer by right-clicking the project and elect `Add -> Existing Item...`.
1. Right-click the license file in the solution explorer, select `Properties` and change the `Build Action` to `BundleResource`.

Rebuild the solution after adding the license files and then run the application. If all is well, you should see a map!

### Step 5: Adding an External Data Source

Now let's add an external data source (Shape File) to the map.

1. Download [WorldCapitals.zip](https://gitlab.com/thinkgeo/public/thinkgeo-mobile-maps/-/tree/master/assets/WorldCapitals.zip) shapefile and unzip it in your project under a new folder called `SampleData`.
1. Include those files to the project. Multi-select them and change the Build Action to "EmbeddedResource".

Unfortunately, reading from the filesystem is a bit tricky with Xamarin. So, we will need to take our EmbeddedResource shapefiles and copy them to the LocalApplicationData directory before we can display them on the map. To do this, we can run some code on application startup in `App.xaml.cs`:

```cs
// App.xaml.cs

public App()
{
    InitializeComponent();
}

protected override async void OnStart()
{
    await CopyAssets();

    MainPage = new MainPage(); // Moved from App constructor
}

/// <summary>
/// Copies all embedded resources to the LocalApplicationData directory
/// </summary>
private async Task CopyAssets()
{
    var assembly = IntrospectionExtensions.GetTypeInfo(typeof(App)).Assembly;
    foreach (var resourceName in assembly.GetManifestResourceNames())
    {
        string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);

        // Change the replace value to whatever your project's name is
        string[] parts = resourceName.Replace("ThinkGeoMobileQuickstart.", "").Split('.');

        string localPath = "";
        for (int i = 0; i < parts.Length; i++)
        {
            // Default delimiter to '/' for the directory structure
            var delimiter = "/";

            // Use '.' delimiter for file extensions
Kyle Day's avatar
Kyle Day committed
198 199 200 201
            if (i == parts.Length - 1) delimiter = ".";

            // Don't use a delimiter for the first part
            if (i == 0) delimiter = "";
Kyle Day's avatar
Kyle Day committed
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223

            localPath += $"{delimiter}{parts[i]}";
        }

        string targetFilePath = Path.Combine(appDataPath, localPath);
        string targetDir = Path.GetDirectoryName(targetFilePath);

        if (!Directory.Exists(targetDir)) Directory.CreateDirectory(targetDir);

        if (!File.Exists(targetFilePath))
        {
            using (var targetStream = File.Create(targetFilePath))
            {
                Stream sourceStream = assembly.GetManifestResourceStream(resourceName);
                await sourceStream.CopyToAsync(targetStream);
                sourceStream.Close();
            }
        }
    }
}
```

Kyle Day's avatar
Kyle Day committed
224
There's a few important notes about the above code. First, we need to copy these files asynchronously, so we needed to add the async keyword to the `OnStart` method. Second, we moved the assignment of the `MainPage` to the `OnStart` method after we finished copying the assets. Finally, EmbeddedResources are always named `ProjectName.Path.To.File.txt`. So, when we copy the files to the filesystem, we are stripping `ProjectName.`.
Kyle Day's avatar
Kyle Day committed
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252

Now, that we can copy over the WorldCapital shapefile, let's add it to the map:

```cs
// MainPage.xaml.cs
protected override void OnAppearing()
{
    base.OnAppearing();

    //...

    // Create a new Feature Layer using the WorldCapitals.shp Shapefile.
    ShapeFileFeatureLayer worldCapitalsFeatureLayer = new ShapeFileFeatureLayer(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "SampleData/WorldCapitals.shp"));
    // Set the pointstyle to black circle with the size of 8.
    worldCapitalsFeatureLayer.ZoomLevelSet.ZoomLevel01.DefaultPointStyle = PointStyle.CreateSimpleCircleStyle(GeoColors.White, 8, GeoColors.Black);
    // Apply the point style from zoomlevel01 to zoomlevel20, that's across all the zoomlevels.
    worldCapitalsFeatureLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;

    // Convert the world capital featurelayer from DecimalDegrees, which is the projection of the raw data, to Spherical Mercator, which is the projection of the map.
    worldCapitalsFeatureLayer.FeatureSource.ProjectionConverter = new ProjectionConverter(Projection.GetDecimalDegreesProjString(), Projection.GetSphericalMercatorProjString());

    // Add the Layer to an Overlay and add the overlay to the map.
    LayerOverlay layerOverlay = new LayerOverlay();
    layerOverlay.Layers.Add(worldCapitalsFeatureLayer);
    mapView.Overlays.Add(layerOverlay);
}
```

Kyle Day's avatar
Kyle Day committed
253 254
Now, when you run the application, the shapefiles will be added to the LocalApplicationData directory which then allows it to be displayed on the map!

Kyle Day's avatar
Kyle Day committed
255 256 257 258 259 260 261 262 263
### Summary

You now know the basics of using the ThinkGeo Map controls and are able to get started adding functionality into your own applications. Let's recap what we have learned about the object relationships and how the pieces of ThinkGeo UI work together:

1. It is of the utmost importance that the units (feet, meters, decimal degrees, etc.) be set properly for the Map control based on the data.
1. FeatureLayers provide the data used by a Map control to render a map.
1. A Map is the basic control that contains all of the other objects that are used to tell how the map is to be rendered.
1. A Map has many layers. A Layer correlates one-to-one with a single data source and typically of one type (point, polygon, line etc).
1. A FeatureLayer can have several ZoomLevels. ZoomLevels help to define ranges (upper and lower) of when a Layer should be shown or hidden.
264

Kyle Day's avatar
Kyle Day committed
265
You are now in a great position to look over the [other samples available](https://gitlab.com/thinkgeo/public/thinkgeo-mobile-maps/-/tree/master/samples/xamarin-forms) and explore our other features.
266 267 268 269 270

## Quick Start: Display a Simple Map on iOS

This will introduce you to ThinkGeo Mobile Maps by getting a nice looking map up and running with ThinkGeo background map along with some external data on a Xamarin iOS application. By the end of this guide, you should have a basic understanding of how to use the Mobile Maps controls.

Ryan Duan's avatar
Ryan Duan committed
271
![Simple Map](https://gitlab.com/thinkgeo/public/thinkgeo-mobile-maps/-/raw/master/assets/ios_quickstart_shapefile_pointstyle_screenshot.png)
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292

### Step 1: Install Prerequisites

In order to develop and debug Xamarin iOS applications, you'll need to have a few prerequisites set up.

To develop on Mac, You need:

* XCode, which provides iOS emulator.
* A development IDE, it could be Visual Studio for Mac, Xamarin Studio or others.
* Xamarin. It has been installed by default in Visual Studio for Mac or Xamarin Studio, might need to be manually installed in other IDEs.
* A provisioning profile is needed if you want to test on an iOS device.

To develop on Windows, you need:

* A Mac Machine in the same network as your build server. XCode needs to be installed on that machine.
* And on your Windows machine, you need:
  * A development IDE, such as Visual Studio or Xamarin Studio
  * Xamarin. Make sure it is well installed.

Even it sounds complicated, Microsoft in fact has made it very straightforward to connect to a MAC and develop Xamarin on Windows. So if you are a .NET developer get used to Visual Studio, feel free to stay on Windows for Xamarin development. This quick start guide is based on Visual Studio on Windows and there's no problem at all to start your application on Mac.

293 294 295 296 297 298
Here a few handy links for installation and setup of these prerequisites using Visual Studio:

[Xamarin for Visual Studio](https://docs.microsoft.com/en-us/xamarin/get-started/installation)

[Xamarin.iOS Installation](https://docs.microsoft.com/en-us/xamarin/ios/get-started/installation/)

299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
### Step 2: Set Up a New Project

Once these prerequisites have been installed, let's create a new **iOS App (Xamarin)** project in Visual Studio and select **Single View App** template. Here is a [guide to creating a sample project](https://docs.microsoft.com/en-us/xamarin/iOS/get-started/hello-iOS/hello-iOS-quickstart) for reference.

Go ahead and run the project once it is created, and Visual Studio will help you to locate the MacOS server and initialize the connection. If everything goes well, you should see the iOS emulator get launched on Windows and the default project runs in the emulator.

### Step 3: Implement the code

Install NuGet Package `ThinkGeo.UI.iOS` to the project:

```shell
dotnet add package ThinkGeo.UI.iOS
```

Add the required usings to the ViewController.cs file:

```csharp
using ThinkGeo.Core;
using ThinkGeo.iOS.UI
```

Update `ViewDidLoad()` method in `ViewController.cs` as following:

```csharp
public override void ViewDidLoad ()
{
    base.ViewDidLoad ();
    // Perform any additional setup after loading the view, typically from a nib.

    // Creat a new MapView, which is the canvas of the map, and add it to the View.
    MapView mapView = new MapView(View.Frame);
    View.AddSubview(mapView);

    // Set the Map Unit to Meter and set the map's current extent to North America.
    mapView.MapUnit = GeographyUnit.Meter;
    mapView.CurrentExtent = new RectangleShape(-13939426, 6701997, -7812401, 2626987);

    // Create a new ThinkGeoCloud Overlay using Client ID / Client Secret, and add it the overlay to MapView.
    string clientKey = "9ap16imkD_V7fsvDW9I8r8ULxgAB50BX_BnafMEBcKg~";
    string secret = "vtVao9zAcOj00UlGcK7U-efLANfeJKzlPuDB9nw7Bp4K4UxU_PdRDg~~";
    ThinkGeoCloudVectorMapsOverlay thinkGeoCloudMapsOverlay = new ThinkGeoCloudVectorMapsOverlay(clientKey, secret);
    mapView.Overlays.Add(thinkGeoCloudMapsOverlay);

    mapView.Refresh();
}
```

### Step 4: Apply an Evaluation License for Free

Build the project and make sure it builds through. "A license is needed" exception will be thrown if you run it though and here is how to fix it:

1. Run `ThinkGeo.ProductCenter.exe` to open the product center. This can be found in the `bin` folder of the project. (`ThinkGeo.ProductCenter.exe` can only be opened on Windows, there's a CLI version for Mac.)
1. Click on `Log In` in the upper-right corner, input the username/password to login or click `Create a new account` to create a ThinkGeo account for free.
1. Once logged in, click on `ThinkGeo UI Mobile for iOS` tile and then click on `Start Evaluation`(it would be `Activate License` if you already purchased), now you can see a textbox with textholder `Bundle Identifer` on the right.
1. Now we need to generate a license for the project following the steps below:
   * Get the project's bundle identifier in `info.plist`, copy and paste it to the 'bundle dentifier' textbox in product center.
   * Hit 'Create' and save the license file (the file name would be `bundle-identifer.apsuitelicense`) to the solution's root folder.
   * Add the license to the project in the solution explorer by right-clicking the project and elect `Add -> Existing Item...`.
   * Right-click the license file in the solution explorer, select `Properties` and change the `Build Action` to `BundleResource`.

Now go ahead and run the application and the map will be displayed properly.

### Step 5: Adding an External Data Source

Now let's add an external data source (Shape File) to the map.

365
1. Download [WorldCapitals.zip](https://gitlab.com/thinkgeo/public/thinkgeo-mobile-maps/-/tree/master/assets/WorldCapitals.zip) shapefile and unzip it in your project under a new folder called `SampleData`.
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
1. Include those files to the project. Multi-select them and change the Build Action to "Content".
1. Now add the following code to `ViewDidLoad()` method.

```csharp
// Create a new Feature Layer using the WorldCapitals.shp Shapefile.
ShapeFileFeatureLayer worldCapitalsFeatureLayer = new ShapeFileFeatureLayer("SampleData/WorldCapitals.shp");
// Set the pointstyle to black circle with the size of 8.
worldCapitalsFeatureLayer.ZoomLevelSet.ZoomLevel01.DefaultPointStyle = PointStyle.CreateSimpleCircleStyle(GeoColors.White, 8, GeoColors.Black);
// Apply the point style from zoomlevel01 to zoomlevel20, that's accross all the zoomlevels.
worldCapitalsFeatureLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;

// Convert the world capital featurelay from DecimalDegrees, which is the projection of the raw data, to Spherical Mercator, which is the projection of the map.
worldCapitalsFeatureLayer.FeatureSource.ProjectionConverter = new ProjectionConverter(Projection.GetDecimalDegreesProjString(), Projection.GetSphericalMercatorProjString());

// Add the Layer to an Overlay and add the overlay to the map.
LayerOverlay layerOverlay = new LayerOverlay();
layerOverlay.Layers.Add(worldCapitalsFeatureLayer);
mapView.Overlays.Add(layerOverlay);
```

### iOS Summary

You now know the basics of using the ThinkGeo Map controls and are able to get started adding functionality into your own applications. Let's recap what we have learned about the object relationships and how the pieces of ThinkGeo UI work together:

1. It is of the utmost importance that the units (feet, meters, decimal degrees, etc.) be set properly for the Map control based on the data.
1. FeatureLayers provide the data used by a Map control to render a map.
1. A Map is the basic control that contains all of the other objects that are used to tell how the map is to be rendered.
1. A Map has many layers. A Layer correlates one-to-one with a single data source and typically of one type (point, polygon, line etc).
1. A FeatureLayer can have several ZoomLevels. ZoomLevels help to define ranges (upper and lower) of when a Layer should be shown or hidden.

396
You are now in a great position to look over the [other samples available](https://gitlab.com/thinkgeo/public/thinkgeo-mobile-maps/-/tree/master/samples/ios) and explore our other features.
397 398

## Quick Start: Display a Simple Map on Android
399

Kyle Day's avatar
Kyle Day committed
400 401
This will introduce you to ThinkGeo Mobile Maps by getting a nice looking map up and running with some external data and styling on a Xamarin Android application. By the end of this guide, you should have a basic understanding of how to use the Mobile Maps controls.

Ryan Duan's avatar
Ryan Duan committed
402
![alt text](https://gitlab.com/thinkgeo/public/thinkgeo-mobile-maps/-/raw/master/assets/quickstart_shapefile_pointstyle_screenshot.PNG "Simple Map")
Ryan Duan's avatar
Ryan Duan committed
403 404 405 406

### Step 1: Set Up Prerequisites

In order to develop and debug Xamarin Android applications, you'll need to have a few prerequisites set up. These include:
407

Ryan Duan's avatar
Ryan Duan committed
408 409 410
* Xamarin
* The Android SDK
* An Android emulator
411

Ryan Duan's avatar
Ryan Duan committed
412
Here a few handy links for installation and setup of these prerequisites using Visual Studio:
413

Ryan Duan's avatar
Ryan Duan committed
414
[Xamarin for Visual Studio](https://docs.microsoft.com/en-us/xamarin/get-started/installation)
Ryan Duan's avatar
Ryan Duan committed
415

Ryan Duan's avatar
Ryan Duan committed
416
[Android SDK](https://docs.microsoft.com/en-us/xamarin/android/get-started/installation/android-sdk)
Ryan Duan's avatar
Ryan Duan committed
417

Ryan Duan's avatar
Ryan Duan committed
418
[Android Emulator](https://docs.microsoft.com/en-us/xamarin/android/get-started/installation/android-emulator/device-manager)
419

420
### Step 2: Set Up a New Project
421

422
Once these prerequisites have been installed, you'll need to create a new **Xamarin Android** project in your editor of choice. Please refer to your editor's instructions on how to create this project. Here is a [guide to creating a sample project](https://docs.microsoft.com/en-us/xamarin/android/get-started/hello-android/hello-android-quickstart) using Visual Studio for reference.
423

424
### Step 3: Add NuGet Packages
425

426
You'll need to install the **ThinkGeo.UI.Android** NuGet package. We strongly suggest you use your editor's [built in NuGet package manager](https://docs.microsoft.com/en-us/nuget/quickstart/install-and-use-a-package-in-visual-studio) if possible.  If you're not using an IDE you can [install it via the the dotnet CLI](https://docs.microsoft.com/en-us/nuget/consume-packages/install-use-packages-dotnet-cli) from inside the project folder where your project file exists.
427

Ryan Duan's avatar
Ryan Duan committed
428 429 430 431
```shell
dotnet add package ThinkGeo.UI.Android
```

432
### Step 4: Set up the App Template and add the MapView element
Ryan Duan's avatar
Ryan Duan committed
433

Ryan Duan's avatar
Ryan Duan committed
434
Open up the main app layout file. In Visual Studio, this should be under the path `Resources\layout\activity_main.axml`, and add the `ThinkGeo.UI.Android.MapView` element
435 436 437

```xml
<?xml version="1.0" encoding="utf-8"?>
Ryan Duan's avatar
Ryan Duan committed
438
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
Ryan Duan's avatar
Ryan Duan committed
439 440 441 442 443 444
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ThinkGeo.UI.Android.MapView
        android:id="@+id/androidMap"
445
        android:layout_width="fill_parent"
Ryan Duan's avatar
Ryan Duan committed
446
        android:layout_height="fill_parent" />
Ryan Duan's avatar
Ryan Duan committed
447
</RelativeLayout>
448 449
```

450
### Step 5: Add Namespaces to MainActivity.cs
451

Ryan Duan's avatar
Ryan Duan committed
452
Add the required usings to the MainActivity.cs file:
453

Ryan Duan's avatar
Ryan Duan committed
454 455
```csharp
using ThinkGeo.Core;
Ryan Duan's avatar
Ryan Duan committed
456
using ThinkGeo.UI.Android;
Ryan Duan's avatar
Ryan Duan committed
457
```
458

459
### Step 6: Add the Map Background Overlay
460

Ryan Duan's avatar
Ryan Duan committed
461
Create a new method called `ShowMap` in the MainActivity.cs file, and add the code below:
462 463

```csharp
Ryan Duan's avatar
Ryan Duan committed
464 465 466 467
public void ShowMap()
{
    // Set our view from the "main" layout resource
    SetContentView(Resource.Layout.activity_main);
Ryan Duan's avatar
Ryan Duan committed
468

Ryan Duan's avatar
Ryan Duan committed
469
    MapView androidMap = FindViewById<MapView>(Resource.Id.androidMap);
Ryan Duan's avatar
Ryan Duan committed
470

Ryan Duan's avatar
Ryan Duan committed
471 472 473 474
    // Set the Map Configuration.
    androidMap.MapUnit = GeographyUnit.Meter;
    androidMap.ZoomLevelSet = new ThinkGeoCloudMapsZoomLevelSet();
    androidMap.CurrentExtent = new RectangleShape(-20000000, 20000000, 20000000, -20000000);
Ryan Duan's avatar
Ryan Duan committed
475

Ryan Duan's avatar
Ryan Duan committed
476 477
    // Add the Cloud Maps Overlay
    ThinkGeoCloudRasterMapsOverlay thinkGeoCloudMapsOverlay = new ThinkGeoCloudRasterMapsOverlay("9ap16imkD_V7fsvDW9I8r8ULxgAB50BX_BnafMEBcKg~", "vtVao9zAcOj00UlGcK7U-efLANfeJKzlPuDB9nw7Bp4K4UxU_PdRDg~~");
Ryan Duan's avatar
Ryan Duan committed
478

Ryan Duan's avatar
Ryan Duan committed
479 480
    androidMap.Overlays.Add("CloudRasterMapsOverlay", thinkGeoCloudMapsOverlay);
}
481 482
```

Ryan Duan's avatar
Ryan Duan committed
483
Then, remove the `SetContentView` call and call this method from the `OnCreate` method in the MainActivity.cs file:
484 485

```csharp
Ryan Duan's avatar
Ryan Duan committed
486 487
// Remove this call from 'OnCreate'
// SetContentView(Resource.Layout.activity_main);
Ryan Duan's avatar
Ryan Duan committed
488

Ryan Duan's avatar
Ryan Duan committed
489 490
// Add this call to the 'OnCreate' method
ShowMap();
Ryan Duan's avatar
Ryan Duan committed
491
```
Kyle Day's avatar
Kyle Day committed
492

493
### Step 7: Run the Sample & Register For Your Free Evaluation
Kyle Day's avatar
Kyle Day committed
494

Ryan Duan's avatar
Ryan Duan committed
495
The first time you run the application, you will be presented with an error requiring a ThinkGeo license to proceed with running the app. In order to register and generate a license for this project, you'll need to perform the following steps:
496

497 498 499 500 501 502 503 504 505
1. Run the ThinkGeo.ProductCenter.exe to open the product center. This can be found in the `bin` folder of your project at `path\to\project\bin\Debug\`.
1. Click on `Log In` in the upper-right corner and `Create a new account`
1. Follow the steps on the website to register for your account
1. Return to Product Center and log in using your new credentials.
1. Click on the `ThinkGeo UI Mobile for Android` tab and activate an evaluation license.
1. To generate a runtime license for the sample app, you'll need to find the package name for your sample project. In Visual Studio, this can be found by right-clicking on the project in the solution explorer and navigating to `Properties -> Android Manifest -> Package Name`
1. Copy the `Package Name` to the `Runtime License` input box to the right of the Product Center and click `Create`. Save the mewly created license to the `Assets` folder of the solution (`path\to\project\Assets`).
1. Add the license to the project in the solution explorer by right-clicking on the `Assets` folder and selecting `Add -> Existing Item`.
1. Right-click on the license and select `Properties`. Ensure that the `Build Action` is set to `AndroidAsset`
506

Ryan Duan's avatar
Ryan Duan committed
507
You should now be able to see your app with our Cloud Maps layer!
508

509
### Step 8: Adding an External Data Source - Requesting Permissions
510

Ryan Duan's avatar
Ryan Duan committed
511
Now that you have the basic map set up, you can add custom data to the map. Depending on the data, this can be complex or quite simple. We'll be going over the simple basics of adding custom data.
512

513
Download the [WorldCapitals.zip](https://gitlab.com/thinkgeo/public/thinkgeo-mobile-maps/-/tree/master/assets/WorldCapitals.zip) shapefile data and unzip it in your project under a new folder in the `Assets` folder called `AppData`. In order to move this data into storage on the Android device, we'll need to set up our app to request some basic permissions as well.
514

Ryan Duan's avatar
Ryan Duan committed
515
First, we need to add the required permissions to the Android manifest. This can be done by right-clicking on the project in the solution explorer and navigating to `Properties -> Android Manifest`, and finding `Required Permissions` near the bottom of the page. We need to ensure that the `READ_EXTERNAL_STORAGE` and `WRITE_EXTERNAL_STORAGE` options are checked.
516

Ryan Duan's avatar
Ryan Duan committed
517
Next, we need to set up the method to request permissions. Add the following fields to your MainActivity class:
518

519
```csharp
Ryan Duan's avatar
Ryan Duan committed
520 521 522 523 524 525
readonly string[] StoragePermissions =
{
    Manifest.Permission.ReadExternalStorage,
    Manifest.Permission.WriteExternalStorage
};
const int RequestStorageId = 0;
526
```
527

Ryan Duan's avatar
Ryan Duan committed
528
Add the following usings:
529

530
```csharp
Ryan Duan's avatar
Ryan Duan committed
531 532
using Android;
using Android.Content.PM;
533
```
534

Ryan Duan's avatar
Ryan Duan committed
535
Then, add the following method to your MainActivity.cs class. This method will handle requesting permissions:
536

537
```csharp
Ryan Duan's avatar
Ryan Duan committed
538 539 540 541
public void RequestRequiredPermissions()
{
    const string readPermission = Manifest.Permission.ReadExternalStorage;
    const string writePermission = Manifest.Permission.WriteExternalStorage;
Ryan Duan's avatar
Ryan Duan committed
542

Ryan Duan's avatar
Ryan Duan committed
543 544 545 546 547 548 549
    if (!(CheckSelfPermission(readPermission) == (int)Permission.Granted) || !(CheckSelfPermission(writePermission) == (int)Permission.Granted))
    {
        RequestPermissions(StoragePermissions, RequestStorageId);
    }
    else
    {
        ShowMap();
Ryan Duan's avatar
Ryan Duan committed
550
    }
Ryan Duan's avatar
Ryan Duan committed
551
}
Ryan Duan's avatar
Ryan Duan committed
552
```
553

554
Add the following code to the `OnRequestPermissionsResult` method in the `MainActivity.cs`:
555

Ryan Duan's avatar
Ryan Duan committed
556
```csharp
Ryan Duan's avatar
Ryan Duan committed
557 558 559
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults)
{
    switch (requestCode)
Ryan Duan's avatar
Ryan Duan committed
560
    {
Ryan Duan's avatar
Ryan Duan committed
561 562 563
        case RequestStorageId:
            {
                if(grantResults.Length > 0 && grantResults[0] == Permission.Granted)
Ryan Duan's avatar
Ryan Duan committed
564
                {
Ryan Duan's avatar
Ryan Duan committed
565
                    ShowMap();
Ryan Duan's avatar
Ryan Duan committed
566
                }
Ryan Duan's avatar
Ryan Duan committed
567 568 569 570 571 572 573
                else
                {
                    Toast.MakeText(this,
                        "Storage Permissions Denied", ToastLength.Short).Show();
                }
            }
            break;
Ryan Duan's avatar
Ryan Duan committed
574
    }
Ryan Duan's avatar
Ryan Duan committed
575
}
Ryan Duan's avatar
Ryan Duan committed
576
```
577

Ryan Duan's avatar
Ryan Duan committed
578
Finally, replace the `ShowMap` call in the `OnCreate` method with a call to the `RequestRequiredPermissions` method:
579

Ryan Duan's avatar
Ryan Duan committed
580
```csharp
Ryan Duan's avatar
Ryan Duan committed
581 582
// Replace 'ShowMap()' in the 'OnCreate' method
RequestRequiredPermissions();
Ryan Duan's avatar
Ryan Duan committed
583
```
584

585
### Step 9: Adding an External Data Source - Importing Data
586

Kyle Day's avatar
Kyle Day committed
587
Now that we have storage permissions set up, we can store the data locally on the Android device. Create a new folder named `SampleData` under the `Assets` folder in the solution, then add the map data to it. Make sure the resources� build action is `AndroidAsset`.
588

Ryan Duan's avatar
Ryan Duan committed
589
Now, we can add a method to copy the data to the external storage for the application to use.
590

Ryan Duan's avatar
Ryan Duan committed
591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610
```csharp
private void CopySampleData(string targetDirectory)
{
    if (!Directory.Exists(targetDirectory)) Directory.CreateDirectory(targetDirectory);

    foreach (string filename in Assets.List("SampleData"))
    {
        string sourcePathFilename = Path.Combine("SampleData", filename);
        string targetPathFilename = Path.Combine(targetDirectory, filename);
        if (!File.Exists(targetPathFilename))
        {
            string targetPath = Path.GetDirectoryName(targetPathFilename);
            if (!Directory.Exists(targetPath)) Directory.CreateDirectory(targetPath);
            Stream sourceStream = Assets.Open(sourcePathFilename);
            FileStream fileStream = File.Create(targetPathFilename);
            sourceStream.CopyTo(fileStream);
            fileStream.Close();
            sourceStream.Close();
        }
    }
611
}
612
```
613

Ryan Duan's avatar
Ryan Duan committed
614
Now we can call this method when we initialize our map, in the `ShowMap` method.
615

616
```csharp
Ryan Duan's avatar
Ryan Duan committed
617 618 619 620 621 622 623
public void ShowMap()
{
    // Set our view from the "main" layout resource
    SetContentView(Resource.Layout.activity_main);
    // Copy the required Shapefiles to Device.
    string targetDirectory = Path.Combine(Environment.ExternalStorageDirectory.ToString(), "SampleData");
    CopySampleData(targetDirectory);
Ryan Duan's avatar
Ryan Duan committed
624
```
625

Ryan Duan's avatar
Ryan Duan committed
626
This method will copy data to the target path, if the folder does not exist.
627

628
### Step 10: Add a Point Data Layer
629

Ryan Duan's avatar
Ryan Duan committed
630
Now we can add the data from the shapefile to the map, in the `ShowMap()` method:
631

Ryan Duan's avatar
Ryan Duan committed
632
```csharp
Ryan Duan's avatar
Ryan Duan committed
633 634
// Add a shapefile layer with point style.
var capitalLayer = new ShapeFileFeatureLayer(Path.Combine(Environment.ExternalStorageDirectory.ToString(), @"SampleData/WorldCapitals.shp"));
635

Ryan Duan's avatar
Ryan Duan committed
636 637 638 639
// Create an overlay to add the layer to and add that overlay to the map.
var customDataOverlay = new LayerOverlay();
customDataOverlay.Layers.Add(capitalLayer);
androidMap.Overlays.Add(customDataOverlay);
Ryan Duan's avatar
Ryan Duan committed
640
```
641

642
### Step 11: Styling and Labeling the Data
643

Ryan Duan's avatar
Ryan Duan committed
644
We won't be able to see the points until a style is defined for it. Adding a style is very straightforward, but extremely extensible and powerful.
645

Ryan Duan's avatar
Ryan Duan committed
646
```csharp
Ryan Duan's avatar
Ryan Duan committed
647 648 649 650 651 652 653 654 655 656
var capitalStyle = new PointStyle()
{
    SymbolType = PointSymbolType.Circle,
    SymbolSize = 8,
    FillBrush = new GeoSolidBrush(GeoColors.White),
    OutlinePen = new GeoPen(GeoColors.Black, 2)
};

capitalLayer.ZoomLevelSet.ZoomLevel01.DefaultPointStyle = capitalStyle;
capitalLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;
657
```
658

659
### Step 12: Reprojecting the Data
660

Ryan Duan's avatar
Ryan Duan committed
661
If you run the app now, you'll notice that there is just a single point shape in the center of the map! This is because the data is in a completely different projection from the map. We can easily fix that, though, by adding a `ProjectionConverter` to the layer from Decimal Degrees(4326) to Spherical Mercator(3857).
662

663
```csharp
Ryan Duan's avatar
Ryan Duan committed
664 665
// Set the projection of the capitalLayer to Spherical Mercator
capitalLayer.FeatureSource.ProjectionConverter = new ProjectionConverter(4326, 3857);
Ryan Duan's avatar
Ryan Duan committed
666
```
667

Ryan Duan's avatar
Ryan Duan committed
668
Now, the data shows up properly on the map!
669

670
### Step 13: Zoom Into the Data
671

Ryan Duan's avatar
Ryan Duan committed
672
Now, we can make the map zoom into an area based on the extent of the data we added above. In order to do that, we must first open the layer for spatial queries to be made.
673

Ryan Duan's avatar
Ryan Duan committed
674
```csharp
Ryan Duan's avatar
Ryan Duan committed
675 676 677
// Open capitalLayer for it to be ready for spatial queries. Then, set the extent of the map to the full view of the data.
capitalLayer.Open();
mapView.CurrentExtent = capitalLayer.GetBoundingBox();
678
```
679

680
### Android Summary
681

Ryan Duan's avatar
Ryan Duan committed
682
You now know the basics of using the ThinkGeo Map controls and are able to get started adding functionality into your own applications. Let's recap what we have learned about the object relationships and how the pieces of ThinkGeo UI work together:
683

Ryan Duan's avatar
Ryan Duan committed
684 685 686 687 688
1. It is of the utmost importance that the units (feet, meters, decimal degrees, etc.) be set properly for the Map control based on the data.
1. FeatureLayers provide the data used by a Map control to render a map.
1. A Map is the basic control that contains all of the other objects that are used to tell how the map is to be rendered.
1. A Map has many layers. A Layer correlates one-to-one with a single data source and typically of one type (point, polygon, line etc).
1. A FeatureLayer can have several ZoomLevels. ZoomLevels help to define ranges (upper and lower) of when a Layer should be shown or hidden.
689

690
You are now in a great position to look over the [other samples available](https://gitlab.com/thinkgeo/public/thinkgeo-mobile-maps/-/tree/master/samples/android) and explore our other features.
691

692
## Need Help?
693

694
If you run into any issues with running the samples, please let us know in the [Community Forums](https://community.thinkgeo.com).
David Rehagen's avatar
David Rehagen committed
695

696
If you have any questions about the product or sales, please contact us at [sales@thinkgeo.com](mailto:sales@thinkgeo.com).