package mx.trackermap.TrackerMap.android.map import android.graphics.Bitmap import android.graphics.Color import android.os.Bundle import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.core.content.ContextCompat import androidx.core.content.res.ResourcesCompat import androidx.core.graphics.drawable.toBitmap import com.mousebird.maply.* import mil.nga.sf.Polygon import mil.nga.sf.util.SFException import mil.nga.sf.wkt.GeometryReader import java.io.File import mx.trackermap.TrackerMap.android.R import mx.trackermap.TrackerMap.client.models.Geofence import mx.trackermap.TrackerMap.utils.MarkerType typealias MarkerCallback = (Int?) -> Unit class MapFragment : GlobeMapFragment() { private var loader: QuadImageLoader? = null data class Marker( val id: Int, val latitude: Double, val longitude: Double, val type: MarkerType = MarkerType.DEFAULT ) var markerCallback: MarkerCallback? = null private val objects = mutableListOf() private val geofenceObjects = mutableListOf() override fun chooseDisplayType(): MapDisplayType { return MapDisplayType.Map } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { super.onCreateView(inflater, container, savedInstanceState) return baseControl.contentView!! } override fun controlHasStarted() { val cacheDirName = "stamen_watercolor6" val cacheDir = File(activity!!.cacheDir, cacheDirName) cacheDir.mkdir() val tileInfo = RemoteTileInfoNew( getString(R.string.maps_streets_tile_url), 0, 21 ) tileInfo.cacheDir = cacheDir val params = SamplingParams() params.coordSystem = SphericalMercatorCoordSystem() params.coverPoles = true params.edgeMatching = true params.minZoom = tileInfo.minZoom params.maxZoom = tileInfo.maxZoom params.singleLevel = true params.maxTiles = 25 loader = QuadImageLoader(params, tileInfo, baseControl) loader?.setImageFormat(RenderController.ImageFormat.MaplyImageUShort565) val clusterGenerator = BasicClusterGenerator( intArrayOf( Color.argb(255, 255, 255, 255) ), 0, Point2d(64.0, 64.0), baseControl, activity ) clusterGenerator.cacheBitmaps(true) clusterGenerator.setExponentBase(2.5) clusterGenerator.setTextColor(Color.BLACK) clusterGenerator.setLayoutSize(Point2d(80.0, 80.0)) clusterGenerator.setSelectable(false) baseControl.clearClusterGenerators() baseControl.addClusterGenerator(clusterGenerator) val latitude = 23.191 val longitude = -100.36 focusOn(latitude, longitude, zoom = 0.4, animated = false) } override fun userDidSelect( mapControl: MapController?, selObjs: Array?, loc: Point2d?, screenLoc: Point2d? ) { super.userDidSelect(mapControl, selObjs, loc, screenLoc) selObjs?.forEach { selectedObject -> if (selectedObject.selObj is ScreenMarker) { val screenMarker = selectedObject.selObj as ScreenMarker val markerId = screenMarker.userObject as Int Log.d("MapFragment", "Selected marker with id: $markerId") markerCallback?.let { it(markerId) } } } } override fun userDidTap(mapControl: MapController?, loc: Point2d?, screenLoc: Point2d?) { super.userDidTap(mapControl, loc, screenLoc) markerCallback?.let { it(null) } } private fun clear(geofences: Boolean = false) { if (geofences) { mapControl.removeObjects( geofenceObjects, ThreadMode.ThreadAny ) geofenceObjects.clear() } else { mapControl.removeObjects( objects, ThreadMode.ThreadAny ) objects.clear() } } fun display(markers: Array, isReport: Boolean) { Log.d("MapFragment", "Displaying markers") clear() val points = markers.map { marker -> Point2d.FromDegrees(marker.longitude, marker.latitude) }.toTypedArray() val vectorInfo = VectorInfo() vectorInfo.setColor(ContextCompat.getColor(context!!, R.color.colorReport)) vectorInfo.setLineWidth(20.0f) /* Draw markers for positions */ val screenMarkers = markers.mapIndexed { i, marker -> val screenMarker = ScreenMarker() screenMarker.loc = Point2d.FromDegrees(marker.longitude, marker.latitude) screenMarker.image = if (isReport) { // For reports, position, start and end, icons must be different when (i) { 0 -> getIcon(MarkerType.REPORT_START) markers.size - 1 -> getIcon(MarkerType.REPORT_END) else -> getIcon(MarkerType.REPORT_POSITION) } } else getIcon(marker.type) screenMarker.size = if (isReport) { // For reports, position, start and end, size must be different when (i) { 0 -> Point2d(144.0, 144.0) markers.size - 1 -> Point2d(144.0, 144.0) else -> Point2d(82.0, 82.0) } } else Point2d(144.0, 144.0) screenMarker.userObject = marker.id screenMarker.selectable = true if (!isReport) { screenMarker.layoutImportance = 1.0f } screenMarker } val markerInfo = MarkerInfo() if (!isReport) { markerInfo.setClusterGroup(0) } objects.add(mapControl.addScreenMarkers( screenMarkers, markerInfo, ThreadMode.ThreadAny )) /* Draw polyline for report */ if (isReport && markers.isNotEmpty()) { val vector = VectorObject() vector.addAreal(points) objects.add(mapControl.addVector( vector, vectorInfo, ThreadMode.ThreadAny )) } // Center map to bounds if (markers.isNotEmpty()) { val mbr = Mbr() points.forEach { mbr.addPoint(it) } mbr.expandByFraction(0.1) mapControl.addPostSurfaceRunnable { val zoom = mapControl.findHeightToViewBounds(mbr, mbr.middle()) mapControl.setPositionGeo(mbr.middle(), zoom) } } } fun displayGeofences(geofences: Array) { Log.d("MapFragment", "Displaying geofences") clear(true) val color = ContextCompat.getColor(context!!, R.color.colorGeofence) val vectorInfo = VectorInfo() vectorInfo.setColor(color) vectorInfo.setLineWidth(10.0f) val labelInfo = LabelInfo() labelInfo.textColor = color val shapes = mutableListOf() val vectors = mutableListOf() val labels = mutableListOf() geofences.forEach { geofence -> geofence.area?.let { area -> try { val geometry = GeometryReader.readGeometry(area) if (!geometry.isEmpty) { when (geometry) { is Polygon -> { val vector = VectorObject() vector.addAreal(geometry.exteriorRing.points.map { Point2d.FromDegrees(it.y, it.x) }.toTypedArray()) vectors.add(vector) val label = ScreenLabel() label.text = geofence.name label.loc = Point2d.FromDegrees(geometry.centroid.y, geometry.centroid.x) label.layoutImportance = Float.MAX_VALUE labels.add(label) } } } } catch (e: SFException) {} } } geofenceObjects.add(mapControl.addShapes( shapes, ShapeInfo(), ThreadMode.ThreadAny )) geofenceObjects.add(mapControl.addVectors( vectors, vectorInfo, ThreadMode.ThreadAny )) geofenceObjects.add(mapControl.addScreenLabels( labels, labelInfo, ThreadMode.ThreadAny )) } fun focusOn(latitude: Double, longitude: Double, zoom: Double = 0.0000144, animated: Boolean = true) { val lat = latitude * Math.PI / 180 val lon = longitude * Math.PI / 180 if (animated) { mapControl.animatePositionGeo(lon, lat, zoom, 0.2) } else { mapControl.setPositionGeo(lon, lat, zoom) } } fun setTileInfo(tileInfo: TileInfoNew) { loader?.changeTileInfo(tileInfo) } private fun getIcon(markerType: MarkerType): Bitmap { return ResourcesCompat.getDrawable( activity!!.resources, when (markerType) { MarkerType.ANIMAL -> R.drawable.map_animal MarkerType.BICYCLE -> R.drawable.map_bicycle MarkerType.BOAT -> R.drawable.map_boat MarkerType.BUS -> R.drawable.map_bus MarkerType.CAR -> R.drawable.map_car MarkerType.CRANE -> R.drawable.map_crane MarkerType.DEFAULT -> R.drawable.map_default MarkerType.HELICOPTER -> R.drawable.map_helicopter MarkerType.MOTORCYCLE -> R.drawable.map_motorcycle MarkerType.OFFROAD -> R.drawable.map_offroad MarkerType.PERSON -> R.drawable.map_person MarkerType.PICKUP -> R.drawable.map_pickup MarkerType.PLANE -> R.drawable.map_plane MarkerType.SCOOTER -> R.drawable.map_scooter MarkerType.SHIP -> R.drawable.map_ship MarkerType.TRACTOR -> R.drawable.map_tractor MarkerType.TRAIN -> R.drawable.map_train MarkerType.TRAM -> R.drawable.map_tram MarkerType.TROLLEYBUS -> R.drawable.map_trolleybus MarkerType.TRUCK -> R.drawable.map_truck MarkerType.VAN -> R.drawable.map_van MarkerType.REPORT_POSITION -> R.drawable.map_report_position MarkerType.REPORT_START -> R.drawable.map_report_start MarkerType.REPORT_END -> R.drawable.map_report_end }, activity!!.theme)!!.toBitmap(144, 144) } }