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,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
)
}
}