How can I overlay 3D model over google or iOS maps?

I have a 3D model for building in KML format and i want to import it over google or apple maps to get the user location and find POI.

I have find that google maps v2 [1]: https://developers.google.com/maps/documentation/android/views support 3d objects.

Answers


Although this is a very late answer, I think below sources (for Android/iOS implementations) should help at least for future reference because I don't see much detailed information right now about KML/KMZ files and their structures;

*If the 3D model is just a 3D image created by photo editing tools or if creating 3D image meets with needs, then below solutions can be used to load them via Google Maps or Apple Maps (MapKit) SDKs.

Google Maps

-- For Android

https://developers.google.com/maps/documentation/android-sdk/utility/kml

Below code is taken from “https://github.com/googlemaps/android-maps-utils/blob/master/demo/src/com/google/maps/android/utils/demo/KmlDemoActivity.java”. I am sharing it only to visualize the implementation.

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.maps.android.data.Feature;
import com.google.maps.android.data.kml.KmlContainer;
import com.google.maps.android.data.kml.KmlLayer;
import com.google.maps.android.data.kml.KmlPlacemark;
import com.google.maps.android.data.kml.KmlPolygon;

import org.xmlpull.v1.XmlPullParserException;

import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;

public class KmlDemoActivity extends BaseDemoActivity {

    private GoogleMap mMap;

    protected int getLayoutId() {
        return R.layout.kml_demo;
    }

    public void startDemo () {
        try {
            mMap = getMap();
            //retrieveFileFromResource();
            retrieveFileFromUrl();
        } catch (Exception e) {
            Log.e("Exception caught", e.toString());
        }
    }

    private void retrieveFileFromResource() {
        try {
            KmlLayer kmlLayer = new KmlLayer(mMap, R.raw.campus, getApplicationContext());
            kmlLayer.addLayerToMap();
            moveCameraToKml(kmlLayer);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (XmlPullParserException e) {
            e.printStackTrace();
        }
    }

    private void retrieveFileFromUrl() {
        new DownloadKmlFile(getString(R.string.kml_url)).execute();
    }

    private void moveCameraToKml(KmlLayer kmlLayer) {
        //Retrieve the first container in the KML layer
        KmlContainer container = kmlLayer.getContainers().iterator().next();
        //Retrieve a nested container within the first container
        container = container.getContainers().iterator().next();
        //Retrieve the first placemark in the nested container
        KmlPlacemark placemark = container.getPlacemarks().iterator().next();
        //Retrieve a polygon object in a placemark
        KmlPolygon polygon = (KmlPolygon) placemark.getGeometry();
        //Create LatLngBounds of the outer coordinates of the polygon
        LatLngBounds.Builder builder = new LatLngBounds.Builder();
        for (LatLng latLng : polygon.getOuterBoundaryCoordinates()) {
            builder.include(latLng);
        }

        int width = getResources().getDisplayMetrics().widthPixels;
        int height = getResources().getDisplayMetrics().heightPixels;
        getMap().moveCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), width, height, 1));
    }

    private class DownloadKmlFile extends AsyncTask<String, Void, byte[]> {
        private final String mUrl;

        public DownloadKmlFile(String url) {
            mUrl = url;
        }

        protected byte[] doInBackground(String... params) {
            try {
                InputStream is =  new URL(mUrl).openStream();
                ByteArrayOutputStream buffer = new ByteArrayOutputStream();
                int nRead;
                byte[] data = new byte[16384];
                while ((nRead = is.read(data, 0, data.length)) != -1) {
                    buffer.write(data, 0, nRead);
                }
                buffer.flush();
                return buffer.toByteArray();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }

        protected void onPostExecute(byte[] byteArr) {
            try {
                KmlLayer kmlLayer = new KmlLayer(mMap, new ByteArrayInputStream(byteArr),
                        getApplicationContext());
                kmlLayer.addLayerToMap();
                kmlLayer.setOnFeatureClickListener(new KmlLayer.OnFeatureClickListener() {
                    @Override
                    public void onFeatureClick(Feature feature) {
                        Toast.makeText(KmlDemoActivity.this,
                                "Feature clicked: " + feature.getId(),
                                Toast.LENGTH_SHORT).show();
                    }
                });
                moveCameraToKml(kmlLayer);
            } catch (XmlPullParserException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

-- For iOS

https://developers.google.com/maps/documentation/ios-sdk/utility/kml-geojson Below code is taken from the above url. Again, I am sharing it only to visualize the implementation.

Swift sample

/** Assume that <Google-Maps-iOS-Utils/GMUGeometryRenderer.h> and
    <Google-Maps-iOS-Utils/GMUKMLParser.h> are in the bridging-header file.
*/
let path = Bundle.main.path(forResource: "KML_Sample", ofType: "kml")
let url = URL(fileURLWithPath: path!)
kmlParser = GMUKMLParser(url: url)
kmlParser.parse()

renderer = GMUGeometryRenderer(map: mapView,
                               geometries: kmlParser.placemarks,
                               styles: kmlParser.styles)

renderer.render()

Objective-C sample

#import "GMUKMLParser.h"
#import "GMUGeometryRenderer.h"
...
NSString *path = [[NSBundle mainBundle] pathForResource:@"KML_Sample" ofType:@"kml"];
NSURL *url = [NSURL fileURLWithPath:path];
GMUKMLParser *parser = [[GMUKMLParser alloc] initWithURL:url];
[parser parse];
GMUGeometryRenderer *renderer = [[GMUGeometryRenderer alloc] initWithMap:_mapView
                                                              geometries:parser.placemarks
                                                                  styles:parser.styles];
[renderer render];

Apple Maps (iOS MapKit)

https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/LocationAwarenessPG/MapKit/MapKit.html

But for Apple Maps, I see that a parser needed like below ones to turn KML data structures into drawing code;

https://github.com/mapbox/Simple-KML https://github.com/mojtabacazi/iOS-KMLParser

Or below KMLViewer implementations can be used; (Based on NSXMLParser)

https://github.com/robovm/apple-ios-samples/tree/master/KMLViewer https://github.com/ooper-shlab/KMLViewer-Swift (Also contains Swift versions)


**Otherwise, if 3D model is really a 3D model that is created by <Model> tag (3D models are described in COLLADA file (.dae) and referenced by <link> tag) See below XML format from “https://developers.google.com/kml/documentation/kmlreference#model”;

<Model id="ID">
  <!-- specific to Model -->
  <altitudeMode>clampToGround</altitudeMode>
      <!-- kml:altitudeModeEnum: clampToGround,relativeToGround,or absolute -->
      <!-- or, substitute gx:altitudeMode: clampToSeaFloor, relativeToSeaFloor -->
  <Location>
    <longitude></longitude> <!-- kml:angle180 -->
    <latitude></latitude>   <!-- kml:angle90 -->
    <altitude>0</altitude>  <!-- double -->
  </Location>
  <Orientation>
    <heading>0</heading>    <!-- kml:angle360 -->
    <tilt>0</tilt>          <!-- kml:anglepos180 -->
    <roll>0</roll>          <!-- kml:angle180 -->
  </Orientation>
  <Scale>
    <x>1</x>                <!-- double -->
    <y>1</y>                <!-- double -->
    <z>1</z>                <!-- double -->
  </Scale>
  <Link>...</Link>
  <ResourceMap>
    <Alias>
      <targetHref>...</targetHref>   <!-- anyURI -->
      <sourceHref>...</sourceHref>   <!-- anyURI -->
    </Alias>
  </ResourceMap>
</Model>

Above picture is taken from https://developers.google.com/maps/documentation/android-sdk/utility/kml#supported. As shown there, Google Maps seems to not support 3D models with tags. Google Earth seems to support 3D models but of course it is not suitable for our case. In addition to this, I didn't see any official support from iOS MapKit.

So, below solutions may work for real 3D models;

I've come across with the OSMBuildings based on OpenStreetMap while I was doing research. It is a Javascript implementation but it may also be used on mobile environments by using for example Leaflet JS library. (KML file should be converted to GeoJSON by “leaflet-omnivore” plugin for this purpose and it seems it is supporting 3D models)

https://leafletjs.com https://github.com/OSMBuildings/OSMBuildings

Lastly, I've found a mapping platform called MapBox that accepts KML (by converting it to GeoJSON again). This option is a paid option but I think there is enough limit for small, personal apps to use it for free (50,000 map views/mo). (Again OpenStreetMap-based platform and it seems it is supporting 3D models)

https://www.mapbox.com/mobile/

Although there may be other libraries or platforms to use KMLs/KMZs, I think these are best suitable ones for mobile experience (for both iOS and Android).

Last small note: KMZ is actually a compressed package that contains KML, textures and various resources in it.


Although this is an old question. I think Apple has an example project in the documentation.


Need Your Help

jQuery function after .append

jquery

How to call a function after jQuery .append is completely done?