diff --git a/Runtime/CesiumIonGeocoder.cs b/Runtime/CesiumIonGeocoder.cs new file mode 100644 index 00000000..c7d377b9 --- /dev/null +++ b/Runtime/CesiumIonGeocoder.cs @@ -0,0 +1,112 @@ +using Reinterop; +using System.Collections; +using System.Threading.Tasks; +using Unity.Mathematics; +using UnityEngine; + +namespace CesiumForUnity +{ + /** + * @brief The supported types of requests to geocoding API. + */ + public enum CesiumIonGeocoderRequestType + { + /** + * @brief Perform a full search from a complete query. + */ + Search, + + /** + * @brief Perform a quick search based on partial input, such as while a user + * is typing. + * The search results may be less accurate or exhaustive than using {@link GeocoderRequestType::Search}. + */ + Autocomplete + } + + /** + * @brief The supported providers that can be accessed through ion's geocoder + * API. + */ + public enum CesiumIonGeocoderProviderType + { + /** + * @brief Google geocoder, for use with Google data. + */ + Google, + + /** + * @brief Bing geocoder, for use with Bing data. + */ + Bing, + + /** + * @brief Use the default geocoder as set on the server. Used when neither + * Bing or Google data is used. + */ + Default + }; + + public class CesiumIonGeocoderFeature + { + /** + * @brief The user-friendly display name of this feature. + */ + public string displayName; + + /** + * @brief The Longitude-Latitude-Height coordinates representing this feature. + * + * If the geocoder service returned a bounding box for this result, this will + * return the bounding box. If the geocoder service returned a coordinate for + * this result, this will return a zero-width rectangle at that coordinate. + */ + public double3 positionLlh; + } + + + /** + * @brief Attribution information for a query to a geocoder service. + */ + public class CesiumIonGeocoderAttribution + { + /** + * @brief An HTML string containing the necessary attribution information. + */ + public string html; + + /** + * @brief If true, the credit should be visible in the main credit container. + * Otherwise, it can appear in a popover. + */ + public bool showOnScreen; + }; + + /** + * @brief The result of making a request to a geocoder service. + */ + public class CesiumIonGeocoderResult + { + /** + * @brief Any necessary attributions for this geocoder result. + */ + public CesiumIonGeocoderAttribution[] attributions; + + /** + * @brief The features obtained from this geocoder service, if any. + */ + public CesiumIonGeocoderFeature[] features; + }; + + [ReinteropNativeImplementation("CesiumForUnityNative::CesiumIonGeocoderImpl", "CesiumIonGeocoderImpl.h")] + public partial class CesiumIonGeocoder + { + public partial Task Geocode( + CesiumIonServer ionServer, + string ionToken, + CesiumIonGeocoderProviderType providerType, + CesiumIonGeocoderRequestType requestType, + string query + ); + } +} diff --git a/Runtime/CesiumIonGeocoder.cs.meta b/Runtime/CesiumIonGeocoder.cs.meta new file mode 100644 index 00000000..5307dee0 --- /dev/null +++ b/Runtime/CesiumIonGeocoder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 237a3186b870725408ec532c08216bf4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/ConfigureReinterop.cs b/Runtime/ConfigureReinterop.cs index 6ab71f62..26e0ba05 100644 --- a/Runtime/ConfigureReinterop.cs +++ b/Runtime/ConfigureReinterop.cs @@ -943,6 +943,23 @@ Cesium3DTilesetLoadFailureDetails tilesetDetails TestReinterop.ThrowAnException(); System.Exception exception = null; var message = exception.Message; + + CesiumIonGeocoder geocoder = new CesiumIonGeocoder(); + CesiumIonGeocoderProviderType providerType = CesiumIonGeocoderProviderType.Google; + CesiumIonGeocoderRequestType requestType = CesiumIonGeocoderRequestType.Autocomplete; + TaskCompletionSource geocoderPromise = new TaskCompletionSource(); + geocoderPromise.SetException(new Exception("message")); + CesiumIonGeocoderResult geocoderResult = new CesiumIonGeocoderResult(); + geocoderResult.features = new CesiumIonGeocoderFeature[1]; + CesiumIonGeocoderFeature geocoderFeature = new CesiumIonGeocoderFeature(); + geocoderResult.features[0].displayName = ""; + geocoderResult.features[0].positionLlh = new double3(1, 2, 3); + geocoderResult.attributions = new CesiumIonGeocoderAttribution[1]; + CesiumIonGeocoderAttribution attribution = new CesiumIonGeocoderAttribution(); + attribution.html = ""; + attribution.showOnScreen = true; + geocoderPromise.SetResult(geocoderResult); + Task geocoderTask = geocoderPromise.Task; } } } diff --git a/native~/Runtime/src/CesiumIonGeocoderImpl.cpp b/native~/Runtime/src/CesiumIonGeocoderImpl.cpp new file mode 100644 index 00000000..64e12550 --- /dev/null +++ b/native~/Runtime/src/CesiumIonGeocoderImpl.cpp @@ -0,0 +1,171 @@ +#include "CesiumIonGeocoderImpl.h" + +#include "UnityTilesetExternals.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { +CesiumIonClient::GeocoderProviderType geocoderProviderTypeEnumToNative( + DotNet::CesiumForUnity::CesiumIonGeocoderProviderType providerType) { + switch (providerType) { + case DotNet::CesiumForUnity::CesiumIonGeocoderProviderType::Bing: + return CesiumIonClient::GeocoderProviderType::Bing; + case DotNet::CesiumForUnity::CesiumIonGeocoderProviderType::Google: + return CesiumIonClient::GeocoderProviderType::Google; + case DotNet::CesiumForUnity::CesiumIonGeocoderProviderType::Default: + return CesiumIonClient::GeocoderProviderType::Default; + } + + CESIUM_ASSERT(false && "Invalid CesiumIonGeocoderProviderType value"); + return CesiumIonClient::GeocoderProviderType::Default; +} + +CesiumIonClient::GeocoderRequestType geocoderRequestTypeEnumToNative( + DotNet::CesiumForUnity::CesiumIonGeocoderRequestType requestType) { + switch (requestType) { + case DotNet::CesiumForUnity::CesiumIonGeocoderRequestType::Autocomplete: + return CesiumIonClient::GeocoderRequestType::Autocomplete; + case DotNet::CesiumForUnity::CesiumIonGeocoderRequestType::Search: + return CesiumIonClient::GeocoderRequestType::Search; + } + + CESIUM_ASSERT(false && "Invalid CesiumIonGeocoderRequestType value"); + return CesiumIonClient::GeocoderRequestType::Search; +} +} // namespace + +namespace CesiumForUnityNative { +CesiumIonGeocoderImpl::CesiumIonGeocoderImpl( + const DotNet::CesiumForUnity::CesiumIonGeocoder& geocoder) + : _pConnection(nullptr) {} + +DotNet::System::Threading::Tasks::Task1< + DotNet::CesiumForUnity::CesiumIonGeocoderResult> +CesiumIonGeocoderImpl::Geocode( + const DotNet::CesiumForUnity::CesiumIonGeocoder& /*geocoder*/, + const DotNet::CesiumForUnity::CesiumIonServer& server, + DotNet::System::String ionToken, + DotNet::CesiumForUnity::CesiumIonGeocoderProviderType providerType, + DotNet::CesiumForUnity::CesiumIonGeocoderRequestType requestType, + DotNet::System::String query) { + CesiumAsync::AsyncSystem asyncSystem = getAsyncSystem(); + + DotNet::System::Threading::Tasks::TaskCompletionSource1< + DotNet::CesiumForUnity::CesiumIonGeocoderResult> + promise{}; + + getConnection(asyncSystem, server, ionToken) + .thenImmediately( + [providerType, requestType, query]( + std::shared_ptr&& pConnection) { + return pConnection->geocode( + geocoderProviderTypeEnumToNative(providerType), + geocoderRequestTypeEnumToNative(requestType), + query.ToStlString()); + }) + .thenImmediately([promise](CesiumIonClient::Response< + CesiumIonClient::GeocoderResult>&& response) { + if (!response.errorCode.empty()) { + promise.SetException(DotNet::System::Exception(DotNet::System::String( + "Ion error code received: " + response.errorCode + + ", message: " + response.errorMessage))); + } else { + DotNet::System::Array1< + DotNet::CesiumForUnity::CesiumIonGeocoderAttribution> + attributions(response.value->attributions.size()); + for (size_t i = 0; i < response.value->attributions.size(); i++) { + DotNet::CesiumForUnity::CesiumIonGeocoderAttribution attribution; + attribution.html( + DotNet::System::String(response.value->attributions[i].html)); + attribution.showOnScreen( + response.value->attributions[i].showOnScreen); + attributions.Item((int32_t)i, attribution); + } + + DotNet::System::Array1< + DotNet::CesiumForUnity::CesiumIonGeocoderFeature> + features(response.value->features.size()); + for (size_t i = 0; i < response.value->features.size(); i++) { + DotNet::CesiumForUnity::CesiumIonGeocoderFeature feature; + feature.displayName(DotNet::System::String( + response.value->features[i].displayName)); + CesiumGeospatial::Cartographic point = + response.value->features[i].getCartographic(); + feature.positionLlh(DotNet::Unity::Mathematics::double3( + point.longitude, + point.latitude, + point.height)); + features.Item((int32_t)i, feature); + } + + DotNet::CesiumForUnity::CesiumIonGeocoderResult geocoderResult; + geocoderResult.attributions(attributions); + geocoderResult.features(features); + promise.SetResult(geocoderResult); + } + }); + + return promise.Task(); +} + +CesiumAsync::Future> +CesiumIonGeocoderImpl::getConnection( + const CesiumAsync::AsyncSystem& asyncSystem, + const DotNet::CesiumForUnity::CesiumIonServer& server, + DotNet::System::String ionToken) { + if (this->_pConnection != nullptr) { + return asyncSystem.createResolvedFuture( + std::shared_ptr(this->_pConnection)); + } + + std::shared_ptr pAssetAccessor = + getAssetAccessor(); + + return CesiumIonClient::Connection::appData( + asyncSystem, + pAssetAccessor, + server.apiUrl().ToStlString()) + .thenImmediately( + [ionToken, server, this, asyncSystem, pAssetAccessor]( + CesiumIonClient::Response&& + response) { + if (!response.value) { + return std::shared_ptr(nullptr); + } + + this->_connectionMutex.lock(); + if (this->_pConnection != nullptr) { + // Another query has already created a connection before this one + // returned. + this->_connectionMutex.unlock(); + return this->_pConnection; + } + + this->_pConnection = std::make_shared( + asyncSystem, + pAssetAccessor, + DotNet::System::String::IsNullOrWhiteSpace(ionToken) + ? server.defaultIonAccessToken().ToStlString() + : ionToken.ToStlString(), + *response.value, + server.apiUrl().ToStlString()); + this->_connectionMutex.unlock(); + return this->_pConnection; + }); +} +} // namespace CesiumForUnityNative diff --git a/native~/Runtime/src/CesiumIonGeocoderImpl.h b/native~/Runtime/src/CesiumIonGeocoderImpl.h new file mode 100644 index 00000000..ea7a946e --- /dev/null +++ b/native~/Runtime/src/CesiumIonGeocoderImpl.h @@ -0,0 +1,54 @@ +#pragma once + +#include "CesiumImpl.h" + +#include + +#include +#include + +#include +#include + +namespace DotNet::CesiumForUnity { +class CesiumIonGeocoder; +class CesiumIonGeocoderResult; +enum class CesiumIonGeocoderRequestType; +enum class CesiumIonGeocoderProviderType; +class CesiumIonServer; + +} // namespace DotNet::CesiumForUnity + +namespace CesiumIonClient { +class Connection; +} + +namespace CesiumForUnityNative { + +class CesiumIonGeocoderImpl : public CesiumImpl { +public: + CesiumIonGeocoderImpl( + const DotNet::CesiumForUnity::CesiumIonGeocoder& geocoder); + + DotNet::System::Threading::Tasks::Task1< + DotNet::CesiumForUnity::CesiumIonGeocoderResult> + Geocode( + const DotNet::CesiumForUnity::CesiumIonGeocoder& geocoder, + const DotNet::CesiumForUnity::CesiumIonServer& server, + DotNet::System::String ionToken, + DotNet::CesiumForUnity::CesiumIonGeocoderProviderType providerType, + DotNet::CesiumForUnity::CesiumIonGeocoderRequestType requestType, + DotNet::System::String query); + +private: + CesiumAsync::Future> + getConnection( + const CesiumAsync::AsyncSystem& asyncSystem, + const DotNet::CesiumForUnity::CesiumIonServer& server, + DotNet::System::String ionToken); + + std::shared_ptr _pConnection; + std::mutex _connectionMutex; +}; + +} // namespace CesiumForUnityNative diff --git a/native~/extern/cesium-native b/native~/extern/cesium-native index 95498106..6b8294bf 160000 --- a/native~/extern/cesium-native +++ b/native~/extern/cesium-native @@ -1 +1 @@ -Subproject commit 95498106dbac35de7ca87bc52c926a94b2091938 +Subproject commit 6b8294bfe341adcc96d1561499b2216b1a86c1c6