Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

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.

like image 771
Mina Nabil Avatar asked May 29 '14 11:05

Mina Nabil


Video Answer


1 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> 

KML supported features 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.

like image 67
Erdem Savasci Avatar answered Sep 30 '22 15:09

Erdem Savasci