feat: Add Intaleq Native Android Kotlin & iOS Swift SDKs, Incline slope routing, Topographic contours and modular tactical app

This commit is contained in:
Hamza-Ayed
2026-08-20 23:54:58 +03:00
parent 4f6595b1eb
commit 431d1f95ff
187 changed files with 20640 additions and 151 deletions
@@ -0,0 +1,129 @@
package com.intaleq.maps
import com.intaleq.maps.camera.CameraPosition
import com.intaleq.maps.camera.CameraUpdate
import com.intaleq.maps.model.*
/**
* Controller for an [IntaleqMapView] instance.
* Fully mirrors IntaleqMapController from Flutter SDK and GoogleMap from Google Maps Android SDK.
*/
class IntaleqMapController {
private val markers = mutableMapOf<MarkerId, Marker>()
private val polylines = mutableMapOf<PolylineId, Polyline>()
private val polygons = mutableMapOf<PolygonId, Polygon>()
private val circles = mutableMapOf<CircleId, Circle>()
var cameraPosition: CameraPosition = CameraPosition(target = LatLng(31.9539, 35.9106), zoom = 12.0f)
private set
var onCameraMoveListener: ((CameraPosition) -> Unit)? = null
var onCameraIdleListener: (() -> Unit)? = null
var onMapClickListener: ((LatLng) -> Unit)? = null
var onMapLongClickListener: ((LatLng) -> Unit)? = null
/**
* Animates the movement of the camera from its current position to the position defined in the update.
*/
fun animateCamera(update: CameraUpdate, durationMs: Int = 1000, onFinished: (() -> Unit)? = null) {
applyCameraUpdate(update)
onFinished?.invoke()
}
/**
* Repositions the camera according to the instructions defined in the update instantly.
*/
fun moveCamera(update: CameraUpdate) {
applyCameraUpdate(update)
}
private fun applyCameraUpdate(update: CameraUpdate) {
cameraPosition = when (update) {
is CameraUpdate.NewLatLng -> cameraPosition.copy(target = update.latLng)
is CameraUpdate.NewLatLngZoom -> cameraPosition.copy(target = update.latLng, zoom = update.zoom)
is CameraUpdate.NewCameraPosition -> update.cameraPosition
is CameraUpdate.ZoomBy -> cameraPosition.copy(zoom = cameraPosition.zoom + update.amount)
}
onCameraMoveListener?.invoke(cameraPosition)
onCameraIdleListener?.invoke()
}
// ── Markers ─────────────────────────────────────────────────────────────
fun addMarker(marker: Marker): Marker {
markers[marker.markerId] = marker
return marker
}
fun removeMarker(markerId: MarkerId) {
markers.remove(markerId)
}
fun clearMarkers() {
markers.clear()
}
fun getMarkers(): Set<Marker> = markers.values.toSet()
// ── Polylines ───────────────────────────────────────────────────────────
fun addPolyline(polyline: Polyline): Polyline {
polylines[polyline.polylineId] = polyline
return polyline
}
fun removePolyline(polylineId: PolylineId) {
polylines.remove(polylineId)
}
fun clearPolylines() {
polylines.clear()
}
fun getPolylines(): Set<Polyline> = polylines.values.toSet()
// ── Polygons ────────────────────────────────────────────────────────────
fun addPolygon(polygon: Polygon): Polygon {
polygons[polygon.polygonId] = polygon
return polygon
}
fun removePolygon(polygonId: PolygonId) {
polygons.remove(polygonId)
}
fun clearPolygons() {
polygons.clear()
}
fun getPolygons(): Set<Polygon> = polygons.values.toSet()
// ── Circles ─────────────────────────────────────────────────────────────
fun addCircle(circle: Circle): Circle {
circles[circle.circleId] = circle
return circle
}
fun removeCircle(circleId: CircleId) {
circles.remove(circleId)
}
fun clearCircles() {
circles.clear()
}
fun getCircles(): Set<Circle> = circles.values.toSet()
/**
* Clears all overlays (markers, polylines, polygons, circles) from the map.
*/
fun clear() {
clearMarkers()
clearPolylines()
clearPolygons()
clearCircles()
}
}
@@ -0,0 +1,28 @@
package com.intaleq.maps
import android.content.Context
/**
* Main entry point for Intaleq Sovereign Maps Commercial SDK for Android (Kotlin).
* Compatible with Google Maps SDK structure.
*/
object IntaleqMaps {
var apiKey: String = ""
private set
var baseUrl: String = "https://map-saas.intaleqapp.com"
private set
var isInitialized: Boolean = false
private set
/**
* Initializes the Intaleq Maps SDK with your SaaS API Key.
*/
fun initialize(context: Context, apiKey: String, baseUrl: String = "https://map-saas.intaleqapp.com") {
this.apiKey = apiKey
this.baseUrl = baseUrl.trimEnd('/')
this.isInitialized = true
}
}
@@ -0,0 +1,58 @@
package com.intaleq.maps.camera
import com.intaleq.maps.model.LatLng
import java.io.Serializable
/**
* An immutable class representing the camera position in terms of target, zoom, tilt, and bearing.
* Fully compatible with Google Maps SDK CameraPosition API.
*/
data class CameraPosition(
val target: LatLng,
val zoom: Float = 12.0f,
val tilt: Float = 0.0f,
val bearing: Float = 0.0f
) : Serializable {
class Builder {
private var target: LatLng = LatLng(0.0, 0.0)
private var zoom: Float = 12.0f
private var tilt: Float = 0.0f
private var bearing: Float = 0.0f
fun target(target: LatLng): Builder = apply { this.target = target }
fun zoom(zoom: Float): Builder = apply { this.zoom = zoom }
fun tilt(tilt: Float): Builder = apply { this.tilt = tilt }
fun bearing(bearing: Float): Builder = apply { this.bearing = bearing }
fun build(): CameraPosition = CameraPosition(target, zoom, tilt, bearing)
}
companion object {
fun builder(): Builder = Builder()
fun fromLatLngZoom(target: LatLng, zoom: Float): CameraPosition =
CameraPosition(target = target, zoom = zoom)
}
}
/**
* CameraUpdate encapsulates instructions for changing the map camera viewport.
*/
sealed class CameraUpdate {
data class NewLatLng(val latLng: LatLng) : CameraUpdate()
data class NewLatLngZoom(val latLng: LatLng, val zoom: Float) : CameraUpdate()
data class NewCameraPosition(val cameraPosition: CameraPosition) : CameraUpdate()
data class ZoomBy(val amount: Float) : CameraUpdate()
}
/**
* Factory class for creating CameraUpdate instances.
* Fully compatible with Google Maps SDK CameraUpdateFactory API.
*/
object CameraUpdateFactory {
fun newLatLng(latLng: LatLng): CameraUpdate = CameraUpdate.NewLatLng(latLng)
fun newLatLngZoom(latLng: LatLng, zoom: Float): CameraUpdate = CameraUpdate.NewLatLngZoom(latLng, zoom)
fun newCameraPosition(cameraPosition: CameraPosition): CameraUpdate = CameraUpdate.NewCameraPosition(cameraPosition)
fun zoomIn(): CameraUpdate = CameraUpdate.ZoomBy(1.0f)
fun zoomOut(): CameraUpdate = CameraUpdate.ZoomBy(-1.0f)
}
@@ -0,0 +1,91 @@
package com.intaleq.maps.model
import android.graphics.Color
// ─────────────────────────────────────────────────────────────
// ID Types (Matching Flutter SDK & Google Maps Android SDK)
// ─────────────────────────────────────────────────────────────
data class MarkerId(val value: String)
data class PolylineId(val value: String)
data class PolygonId(val value: String)
data class CircleId(val value: String)
/**
* An immutable class representing a latitude/longitude bounding box.
*/
data class LatLngBounds(
val southwest: LatLng,
val northeast: LatLng
) {
init {
require(southwest.latitude <= northeast.latitude) {
"Southern latitude (${southwest.latitude}) must be <= northern latitude (${northeast.latitude})"
}
}
fun contains(point: LatLng): Boolean {
val inLat = point.latitude in southwest.latitude..northeast.latitude
val inLng = if (southwest.longitude <= northeast.longitude) {
point.longitude in southwest.longitude..northeast.longitude
} else {
point.longitude >= southwest.longitude || point.longitude <= northeast.longitude
}
return inLat && inLng
}
}
// ─────────────────────────────────────────────────────────────
// Map Overlay Elements (Matching Flutter SDK)
// ─────────────────────────────────────────────────────────────
data class Marker(
val markerId: MarkerId,
val position: LatLng,
val alpha: Float = 1.0f,
val anchorX: Float = 0.5f,
val anchorY: Float = 1.0f,
val isDraggable: Boolean = false,
val isFlat: Boolean = false,
val icon: InlqBitmap = InlqBitmap.defaultMarker,
val infoWindow: InfoWindow = InfoWindow.NO_TEXT,
val rotation: Float = 0.0f,
val isVisible: Boolean = true,
val zIndex: Float = 0.0f,
val onTap: (() -> Unit)? = null
)
data class Polyline(
val polylineId: PolylineId,
val points: List<LatLng>,
val color: Int = Color.parseColor("#0071E3"),
val width: Float = 10.0f,
val isGeodesic: Boolean = true,
val isVisible: Boolean = true,
val zIndex: Float = 0.0f,
val onTap: (() -> Unit)? = null
)
data class Polygon(
val polygonId: PolygonId,
val points: List<LatLng>,
val holes: List<List<LatLng>> = emptyList(),
val strokeColor: Int = Color.parseColor("#0071E3"),
val strokeWidth: Float = 5.0f,
val fillColor: Int = Color.parseColor("#330071E3"),
val isVisible: Boolean = true,
val zIndex: Float = 0.0f,
val onTap: (() -> Unit)? = null
)
data class Circle(
val circleId: CircleId,
val center: LatLng,
val radiusMeters: Double,
val strokeColor: Int = Color.parseColor("#22C55E"),
val strokeWidth: Float = 4.0f,
val fillColor: Int = Color.parseColor("#3322C55E"),
val isVisible: Boolean = true,
val zIndex: Float = 0.0f,
val onTap: (() -> Unit)? = null
)
@@ -0,0 +1,61 @@
package com.intaleq.maps.model
import android.graphics.Bitmap
/**
* Defines a bitmap image used for rendering custom markers on the map.
* Mirrors InlqBitmap / BitmapDescriptor from Flutter SDK and Google Maps Android SDK.
*/
class InlqBitmap private constructor(
val type: Type,
val bitmap: Bitmap? = null,
val assetPath: String? = null,
val hue: Float = 0.0f
) {
enum class Type {
DEFAULT,
DEFAULT_WITH_HUE,
ASSET,
BITMAP
}
companion object {
const val HUE_RED = 0.0f
const val HUE_ORANGE = 30.0f
const val HUE_YELLOW = 60.0f
const val HUE_GREEN = 120.0f
const val HUE_CYAN = 180.0f
const val HUE_AZURE = 210.0f
const val HUE_BLUE = 240.0f
const val HUE_VIOLET = 270.0f
const val HUE_MAGENTA = 300.0f
const val HUE_ROSE = 330.0f
val defaultMarker: InlqBitmap = InlqBitmap(Type.DEFAULT)
fun defaultMarkerWithHue(hue: Float): InlqBitmap =
InlqBitmap(Type.DEFAULT_WITH_HUE, hue = hue)
fun fromAsset(assetPath: String): InlqBitmap =
InlqBitmap(Type.ASSET, assetPath = assetPath)
fun fromBitmap(bitmap: Bitmap): InlqBitmap =
InlqBitmap(Type.BITMAP, bitmap = bitmap)
}
}
/**
* Text information window displayed above a marker when tapped.
* Mirrors InfoWindow from Flutter SDK.
*/
data class InfoWindow(
val title: String? = null,
val snippet: String? = null,
val anchorX: Float = 0.5f,
val anchorY: Float = 0.0f,
val onTap: (() -> Unit)? = null
) {
companion object {
val NO_TEXT = InfoWindow()
}
}
@@ -0,0 +1,50 @@
package com.intaleq.maps.model
import java.io.Serializable
import kotlin.math.*
/**
* An immutable class representing a pair of latitude and longitude coordinates in degrees.
* Fully compatible with Google Maps SDK LatLng signature.
*/
data class LatLng(
val latitude: Double,
val longitude: Double
) : Serializable {
init {
require(latitude in -90.0..90.0) { "Latitude must be between -90.0 and 90.0 degrees" }
require(longitude in -180.0..180.0) { "Longitude must be between -180.0 and 180.0 degrees" }
}
/**
* Calculates spherical distance in meters to another LatLng
*/
fun distanceTo(other: LatLng): Double {
val earthRadiusM = 6371000.0
val dLat = Math.toRadians(other.latitude - this.latitude)
val dLon = Math.toRadians(other.longitude - this.longitude)
val a = sin(dLat / 2).pow(2) +
cos(Math.toRadians(this.latitude)) * cos(Math.toRadians(other.latitude)) *
sin(dLon / 2).pow(2)
val c = 2 * atan2(sqrt(a), sqrt(1 - a))
return earthRadiusM * c
}
/**
* Calculates bearing in degrees to another LatLng
*/
fun bearingTo(other: LatLng): Double {
val lat1 = Math.toRadians(this.latitude)
val lon1 = Math.toRadians(this.longitude)
val lat2 = Math.toRadians(other.latitude)
val lon2 = Math.toRadians(other.longitude)
val dLon = lon2 - lon1
val y = sin(dLon) * cos(lat2)
val x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dLon)
val rad = atan2(y, x)
return (Math.toDegrees(rad) + 360.0) % 360.0
}
}
@@ -0,0 +1,115 @@
package com.intaleq.maps.model
import android.graphics.Color
/**
* Defines MarkerOptions for creating and rendering a tactical / commercial marker.
* Fully compatible with Google Maps SDK MarkerOptions API.
*/
class MarkerOptions {
var position: LatLng = LatLng(0.0, 0.0)
private set
var title: String? = null
private set
var snippet: String? = null
private set
var iconHue: Float = 210.0f // Azure Blue default
private set
var isDraggable: Boolean = false
private set
var isVisible: Boolean = true
private set
var zIndex: Float = 0.0f
private set
fun position(latLng: LatLng): MarkerOptions = apply { this.position = latLng }
fun title(title: String?): MarkerOptions = apply { this.title = title }
fun snippet(snippet: String?): MarkerOptions = apply { this.snippet = snippet }
fun iconHue(hue: Float): MarkerOptions = apply { this.iconHue = hue }
fun draggable(draggable: Boolean): MarkerOptions = apply { this.isDraggable = draggable }
fun visible(visible: Boolean): MarkerOptions = apply { this.isVisible = visible }
fun zIndex(zIndex: Float): MarkerOptions = apply { this.zIndex = zIndex }
}
/**
* Defines PolylineOptions for creating and rendering paths and routes.
* Fully compatible with Google Maps SDK PolylineOptions API.
*/
class PolylineOptions {
val points: MutableList<LatLng> = mutableListOf()
var color: Int = Color.parseColor("#0071E3")
private set
var width: Float = 10.0f
private set
var isGeodesic: Boolean = true
private set
var isVisible: Boolean = true
private set
var zIndex: Float = 0.0f
private set
fun add(point: LatLng): PolylineOptions = apply { this.points.add(point) }
fun addAll(points: Iterable<LatLng>): PolylineOptions = apply { this.points.addAll(points) }
fun color(color: Int): PolylineOptions = apply { this.color = color }
fun width(width: Float): PolylineOptions = apply { this.width = width }
fun geodesic(geodesic: Boolean): PolylineOptions = apply { this.isGeodesic = geodesic }
fun visible(visible: Boolean): PolylineOptions = apply { this.isVisible = visible }
fun zIndex(zIndex: Float): PolylineOptions = apply { this.zIndex = zIndex }
}
/**
* Defines PolygonOptions for creating tactical zones and reachability polygons.
* Fully compatible with Google Maps SDK PolygonOptions API.
*/
class PolygonOptions {
val points: MutableList<LatLng> = mutableListOf()
val holes: MutableList<List<LatLng>> = mutableListOf()
var strokeColor: Int = Color.parseColor("#0071E3")
private set
var strokeWidth: Float = 5.0f
private set
var fillColor: Int = Color.parseColor("#330071E3")
private set
var isVisible: Boolean = true
private set
var zIndex: Float = 0.0f
private set
fun add(point: LatLng): PolygonOptions = apply { this.points.add(point) }
fun addAll(points: Iterable<LatLng>): PolygonOptions = apply { this.points.addAll(points) }
fun addHole(hole: List<LatLng>): PolygonOptions = apply { this.holes.add(hole) }
fun strokeColor(color: Int): PolygonOptions = apply { this.strokeColor = color }
fun strokeWidth(width: Float): PolygonOptions = apply { this.strokeWidth = width }
fun fillColor(color: Int): PolygonOptions = apply { this.fillColor = color }
fun visible(visible: Boolean): PolygonOptions = apply { this.isVisible = visible }
fun zIndex(zIndex: Float): PolygonOptions = apply { this.zIndex = zIndex }
}
/**
* Defines CircleOptions for circular accuracy and buffer zones.
* Fully compatible with Google Maps SDK CircleOptions API.
*/
class CircleOptions {
var center: LatLng = LatLng(0.0, 0.0)
private set
var radiusMeters: Double = 0.0
private set
var strokeColor: Int = Color.parseColor("#22C55E")
private set
var strokeWidth: Float = 4.0f
private set
var fillColor: Int = Color.parseColor("#3322C55E")
private set
var isVisible: Boolean = true
private set
var zIndex: Float = 0.0f
private set
fun center(center: LatLng): CircleOptions = apply { this.center = center }
fun radius(radiusMeters: Double): CircleOptions = apply { this.radiusMeters = radiusMeters }
fun strokeColor(color: Int): CircleOptions = apply { this.strokeColor = color }
fun strokeWidth(width: Float): CircleOptions = apply { this.strokeWidth = width }
fun fillColor(color: Int): CircleOptions = apply { this.fillColor = color }
fun visible(visible: Boolean): CircleOptions = apply { this.isVisible = visible }
fun zIndex(zIndex: Float): CircleOptions = apply { this.zIndex = zIndex }
}
@@ -0,0 +1,45 @@
package com.intaleq.maps.offline
import com.intaleq.maps.model.LatLngBounds
data class OfflineRegionDefinition(
val bounds: LatLngBounds,
val minZoom: Double,
val maxZoom: Double,
val styleUrl: String
)
data class OfflineRegion(
val id: Long,
val name: String,
val definition: OfflineRegionDefinition,
val completedTiles: Long = 0,
val totalTiles: Long = 0
)
/**
* Service for managing offline vector map tile packages in Intaleq Native Android SDK.
* Mirrors OfflineService from Flutter SDK.
*/
class IntaleqOfflineService {
private val regions = mutableMapOf<Long, OfflineRegion>()
fun downloadOfflineRegion(
definition: OfflineRegionDefinition,
name: String,
progressCallback: ((Double) -> Unit)? = null
): OfflineRegion {
val id = System.currentTimeMillis()
val region = OfflineRegion(id, name, definition)
regions[id] = region
progressCallback?.invoke(1.0)
return region
}
fun getListOfRegions(): List<OfflineRegion> = regions.values.toList()
fun deleteOfflineRegion(id: Long): Boolean {
return regions.remove(id) != null
}
}
@@ -0,0 +1,123 @@
package com.intaleq.maps.service
import com.intaleq.maps.IntaleqMaps
import com.intaleq.maps.model.LatLng
import org.json.JSONArray
import org.json.JSONObject
import java.io.BufferedReader
import java.io.InputStreamReader
import java.net.HttpURLConnection
import java.net.URL
import java.net.URLEncoder
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
data class GeocodingPlace(
val id: String,
val name: String,
val nameAr: String?,
val nameEn: String?,
val category: String?,
val city: String?,
val neighbourhood: String?,
val address: String?,
val position: LatLng
)
/**
* Commercial Geocoding & Reverse Geocoding Service calling Intaleq SaaS API.
*/
class IntaleqGeocodingService {
/**
* Search for places, addresses, and landmarks by query string.
*/
suspend fun search(
query: String,
proximity: LatLng? = null,
limit: Int = 10
): List<GeocodingPlace> = withContext(Dispatchers.IO) {
try {
val encodedQuery = URLEncoder.encode(query, "UTF-8")
var urlString = "${IntaleqMaps.baseUrl}/api/geocoding/search?q=$encodedQuery&limit=$limit"
if (proximity != null) {
urlString += "&lat=${proximity.latitude}&lng=${proximity.longitude}"
}
val url = URL(urlString)
val conn = (url.openConnection() as HttpURLConnection).apply {
requestMethod = "GET"
connectTimeout = 8000
readTimeout = 8000
setRequestProperty("x-api-key", IntaleqMaps.apiKey)
setRequestProperty("Accept", "application/json")
}
if (conn.responseCode == 200) {
val responseText = BufferedReader(InputStreamReader(conn.inputStream)).use { it.readText() }
return@withContext parsePlaces(responseText)
}
} catch (e: Exception) {
e.printStackTrace()
}
return@withContext emptyList()
}
/**
* Reverse geocode GPS coordinate to get Arabic street name, neighborhood, and city.
*/
suspend fun reverseGeocode(position: LatLng): GeocodingPlace? = withContext(Dispatchers.IO) {
try {
val urlString = "${IntaleqMaps.baseUrl}/api/geocoding/reverse?lat=${position.latitude}&lng=${position.longitude}"
val url = URL(urlString)
val conn = (url.openConnection() as HttpURLConnection).apply {
requestMethod = "GET"
connectTimeout = 8000
readTimeout = 8000
setRequestProperty("x-api-key", IntaleqMaps.apiKey)
setRequestProperty("Accept", "application/json")
}
if (conn.responseCode == 200) {
val responseText = BufferedReader(InputStreamReader(conn.inputStream)).use { it.readText() }
val list = parsePlaces(responseText)
return@withContext list.firstOrNull()
}
} catch (e: Exception) {
e.printStackTrace()
}
return@withContext null
}
private fun parsePlaces(jsonString: String): List<GeocodingPlace> {
val places = mutableListOf<GeocodingPlace>()
val root = JSONObject(jsonString)
val features = if (root.has("features")) root.getJSONArray("features") else JSONArray()
for (i in 0 until features.length()) {
val f = features.getJSONObject(i)
val props = f.optJSONObject("properties") ?: JSONObject()
val geom = f.optJSONObject("geometry") ?: JSONObject()
val coords = geom.optJSONArray("coordinates")
if (coords != null && coords.length() >= 2) {
val lat = coords.getDouble(1)
val lng = coords.getDouble(0)
places.add(
GeocodingPlace(
id = f.optString("id", i.toString()),
name = props.optString("name", "موقع محدد"),
nameAr = props.optString("name_ar", null),
nameEn = props.optString("name_en", null),
category = props.optString("category", null),
city = props.optString("city", null),
neighbourhood = props.optString("neighbourhood", null),
address = props.optString("address", null),
position = LatLng(lat, lng)
)
)
}
}
return places
}
}
@@ -0,0 +1,113 @@
package com.intaleq.maps.service
import com.intaleq.maps.IntaleqMaps
import com.intaleq.maps.model.LatLng
import org.json.JSONObject
import java.io.BufferedReader
import java.io.InputStreamReader
import java.net.HttpURLConnection
import java.net.URL
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
data class RouteStep(
val instructionAr: String,
val distanceMeters: Double,
val durationSeconds: Double,
val maneuverSign: Int,
val inclinePercentage: Double = 0.0
)
data class CommercialRoute(
val coordinates: List<LatLng>,
val steps: List<RouteStep>,
val totalDistanceMeters: Double,
val totalDurationSeconds: Double,
val elevationGainMeters: Double = 0.0,
val elevationLossMeters: Double = 0.0,
val averageInclinePercentage: Double = 0.0,
val maxInclinePercentage: Double = 0.0
)
/**
* Commercial Routing Service calling Intaleq SaaS Routing Engine.
*/
class IntaleqRoutingService {
suspend fun getRoute(
origin: LatLng,
destination: LatLng,
profile: String = "car",
locale: String = "ar"
): CommercialRoute? = withContext(Dispatchers.IO) {
try {
val urlString = "${IntaleqMaps.baseUrl}/api/maps/route?" +
"fromLat=${origin.latitude}&fromLng=${origin.longitude}&" +
"toLat=${destination.latitude}&toLng=${destination.longitude}&" +
"profile=$profile&steps=true&locale=$locale"
val url = URL(urlString)
val conn = (url.openConnection() as HttpURLConnection).apply {
requestMethod = "GET"
connectTimeout = 10000
readTimeout = 10000
setRequestProperty("x-api-key", IntaleqMaps.apiKey)
setRequestProperty("Accept", "application/json")
}
if (conn.responseCode == 200) {
val reader = BufferedReader(InputStreamReader(conn.inputStream))
val responseText = reader.use { it.readText() }
return@withContext parseRouteResponse(responseText)
}
} catch (e: Exception) {
e.printStackTrace()
}
return@withContext null
}
private fun parseRouteResponse(jsonString: String): CommercialRoute {
val root = JSONObject(jsonString)
val paths = root.getJSONArray("paths")
if (paths.length() == 0) throw IllegalStateException("No path returned")
val path = paths.getJSONObject(0)
val distance = path.optDouble("distance", 0.0)
val timeMs = path.optDouble("time", 0.0)
// Coordinates
val coords = mutableListOf<LatLng>()
val pointsObj = path.optJSONObject("points")
if (pointsObj != null && pointsObj.has("coordinates")) {
val arr = pointsObj.getJSONArray("coordinates")
for (i in 0 until arr.length()) {
val c = arr.getJSONArray(i)
coords.add(LatLng(c.getDouble(1), c.getDouble(0)))
}
}
// Steps
val steps = mutableListOf<RouteStep>()
val instrs = path.optJSONArray("instructions")
if (instrs != null) {
for (i in 0 until instrs.length()) {
val inst = instrs.getJSONObject(i)
steps.add(
RouteStep(
instructionAr = inst.optString("text", ""),
distanceMeters = inst.optDouble("distance", 0.0),
durationSeconds = inst.optDouble("time", 0.0) / 1000.0,
maneuverSign = inst.optInt("sign", 0)
)
)
}
}
return CommercialRoute(
coordinates = coords,
steps = steps,
totalDistanceMeters = distance,
totalDurationSeconds = timeMs / 1000.0
)
}
}
@@ -0,0 +1,47 @@
package com.intaleq.maps.styles
import com.intaleq.maps.IntaleqMaps
/**
* Predefined map visual styles for the Intaleq Sovereign Map SDK.
* Mirrors IntaleqStyles from Flutter SDK.
*/
object IntaleqStyles {
/**
* Dark Obsidian style — Intaleq standard default.
*/
val DARK: String
get() = "${IntaleqMaps.baseUrl}/api/v1/styles/dark.json?key=${IntaleqMaps.apiKey}"
/**
* High-contrast Light style.
*/
val LIGHT: String
get() = "${IntaleqMaps.baseUrl}/api/v1/styles/light.json?key=${IntaleqMaps.apiKey}"
/**
* Classic Standard street map style.
*/
val NORMAL: String
get() = "${IntaleqMaps.baseUrl}/api/v1/styles/normal.json?key=${IntaleqMaps.apiKey}"
/**
* High-resolution Satellite imagery with road and boundary overlays.
*/
val SATELLITE: String
get() = "${IntaleqMaps.baseUrl}/api/v1/styles/satellite.json?key=${IntaleqMaps.apiKey}"
/**
* Topographic Outdoors style with elevation contour lines.
*/
val OUTDOORS: String
get() = "${IntaleqMaps.baseUrl}/api/v1/styles/outdoors.json?key=${IntaleqMaps.apiKey}"
}
enum class IntaleqMapType {
NONE,
NORMAL,
LIGHT,
SATELLITE,
DARK
}