package mx.trackermap.TrackerMap.android.map import android.graphics.Bitmap import android.graphics.Typeface 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 name: String, val latitude: Double, val longitude: Double, val type: MarkerType = MarkerType.DEFAULT ) var markerCallback: MarkerCallback? = null private val objects = mutableListOf() private val geofenceObjects = mutableListOf() private var tileInfo: TileInfoNew? = null 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() { Log.d("MapFragment", "controlHasStarted") val cacheDirName = "stamen_watercolor6" val cacheDir = File(activity!!.cacheDir, cacheDirName) cacheDir.mkdir() if (tileInfo == null) { tileInfo = RemoteTileInfoNew( getString(R.string.maps_streets_tile_url), 0, 21 ) (tileInfo as RemoteTileInfoNew).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 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 fontSize = context?.resources?.getDimensionPixelSize(R.dimen.marker_label_text_size) val colorReport = ContextCompat.getColor(context!!, R.color.colorReport) val colorLabel = ContextCompat.getColor(context!!, R.color.colorMarkerLabel) val colorLabelOutline = ContextCompat.getColor(context!!, R.color.colorMarkerLabelOutline) val vectorInfo = VectorInfo() vectorInfo.setColor(colorReport) vectorInfo.setLineWidth(20.0f) val labelInfo = LabelInfo() labelInfo.typeface = Typeface.DEFAULT_BOLD labelInfo.textColor = colorLabel labelInfo.outlineColor = colorLabelOutline labelInfo.outlineSize = 3.0f fontSize?.let { labelInfo.fontSize = it.toFloat() } /* 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 screenMarker } objects.add(mapControl.addScreenMarkers( screenMarkers, MarkerInfo(), ThreadMode.ThreadAny )) /* Add labels for markers */ if (!isReport && markers.isNotEmpty()) { val screenLabels = markers.map { marker -> val label = ScreenLabel() label.layoutImportance = Float.MAX_VALUE label.text = if (marker.name.length >= 20) { marker.name.substring(0 until 20) + "..." } else marker.name label.loc = Point2d.FromDegrees(marker.longitude, marker.latitude) label.offset = Point2d(0.0, 92.0) label } objects.add(mapControl.addScreenLabels( screenLabels, labelInfo, 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 fontSize = context?.resources?.getDimensionPixelSize(R.dimen.marker_label_text_size) val colorFill = ContextCompat.getColor(context!!, R.color.colorGeofence) val colorLabel = ContextCompat.getColor(context!!, R.color.colorGeofenceLabel) val colorLabelOutline = ContextCompat.getColor(context!!, R.color.colorMarkerLabelOutline) val vectorInfo = VectorInfo() vectorInfo.setColor(colorFill) vectorInfo.setLineWidth(10.0f) val labelInfo = LabelInfo() labelInfo.typeface = Typeface.DEFAULT_BOLD labelInfo.textColor = colorLabel labelInfo.outlineColor = colorLabelOutline labelInfo.outlineSize = 3.0f fontSize?.let { labelInfo.fontSize = it.toFloat() } 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 updateTileInfo(tileInfo: TileInfoNew) { this.tileInfo = tileInfo loader?.changeTileInfo(tileInfo) } private fun getIcon(markerType: MarkerType): Bitmap { return ResourcesCompat.getDrawable( activity!!.resources, MarkerTransformations.markerTypeToResourceId(markerType), activity!!.theme)!!.toBitmap(144, 144) } }