I have the following problem.
I receive in my LocationChangeListeningCallback latitude and longitude but getting the speed is not possible. It is always 0.
Here is my code (I hope I have not omitted anything).
AndroidManidest.xml
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
build.gradle
//MapBox
implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:8.6.2'
implementation('com.google.android.gms:play-services-location:18.0.0')
MainActivity.kt
override fun onMapReady(mapboxMap: MapboxMap) {
map = mapboxMap
callback = LocationChangeListeningCallback()
mapboxMap.setStyle(Style.MAPBOX_STREETS) {
enableLocationComponent(it)
}
}
private inner class LocationChangeListeningCallback : LocationEngineCallback {
override fun onSuccess(result: LocationEngineResult?) {
result?.lastLocation ?: return
if (result.lastLocation != null){
val lat = result.lastLocation?.latitude!!
val lng = result.lastLocation?.longitude!!
val latLng = LatLng(lat, lng)
if (result.lastLocation != null) {
map.locationComponent.forceLocationUpdate(result.lastLocation)
val position = CameraPosition.Builder()
.target(latLng)
.zoom(13.0)
.tilt(10.0)
.build()
map.animateCamera(CameraUpdateFactory.newCameraPosition(position))
point = Point.fromLngLat(lng, lat)
pointList.add(point)
if(pointList.size>1) {
var location = Location(LocationManager.GPS_PROVIDER)
val results = FloatArray(5)
val lastlat = pointList[pointList.lastIndex - 1]
var xLng = lastlat.coordinates()[0]
var yLat = lastlat.coordinates()[1]
Location.distanceBetween(yLat, xLng, lat, lng, results)
//--> Here speed is always 0.0
if(location.hasSpeed())
speed = location.speed/18*5
else
speed = 0F
accDistance += results[0]
textViewDistance.text = "Distance in meters covered: " + accDistance + "\n" + "Current Velocity: " + speed
} else {
textViewDistance.text = "Distance in meters covered: 0.0" + "\n" + "Current Velocity: 0"
}
map_view.getMapAsync(OnMapReadyCallback { mapboxMap ->
mapboxMap.setStyle(Style.MAPBOX_STREETS) { style ->
// Create the LineString from the list of coordinates and then make a GeoJSON
// FeatureCollection so we can add the line to our map as a layer
style.addSource(
GeoJsonSource(
"line-source",
FeatureCollection.fromFeatures(
arrayOf(
Feature.fromGeometry(
LineString.fromLngLats(pointList)
)
)
)
)
)
// The layer properties for our line
style.addLayer(
LineLayer("linelayer", "line-source").withProperties(
PropertyFactory.lineWidth(3f),
PropertyFactory.lineColor(Color.parseColor("#FF0000"))
)
)
}
})
}
}
}
override fun onFailure(exception: Exception) {}
}
I hope somebody can help me.
I have not found anything on the Internet to solve my problem.
I created a ViewRenderable to go underneath my GLB model, but it doesn't seem to result in any shadow being cast on the viewRenderable.
What am I doing wrong?
class ShadowSelectionVisualizer : SelectionVisualizer {
private val footprintNode: Node = Node()
fun setFootprintRenderable(viewRenderable: ViewRenderable) {
val copyRenderable = viewRenderable.makeCopy()
copyRenderable.verticalAlignment = ViewRenderable.VerticalAlignment.CENTER
val rotation1: Quaternion = Quaternion.axisAngle(Vector3(1.0f, 0.0f, 0.0f), 90f)
footprintNode.renderable = copyRenderable
footprintNode.localRotation = rotation1
}
override fun applySelectionVisual(node: BaseTransformableNode) {
if (node.collisionShape is Box) {
val box: Box = node.collisionShape as Box
val vector3: Vector3 = box.size
(footprintNode.renderable as ViewRenderable).sizer = ViewSizer {
val bound = if (vector3.x > vector3.z) vector3.x else vector3.z
Vector3(bound, bound, 0.0f)
}
footprintNode.setParent(node)
}
}
override fun removeSelectionVisual(node: BaseTransformableNode?) {
footprintNode.setParent(null)
}
}
myModelRenderable.isShadowCaster = true
val selectionVisualizer = ShadowSelectionVisualizer()
val transformationSystem = TransformationSystem(resources.displayMetrics, selectionVisualizer)
ViewRenderable.builder()
.setView(
context, R.layout.shadow_receiver
)
.setRegistryId("someId.glb")
.build()
.thenAccept { viewRenderable: ViewRenderable ->
viewRenderable.isShadowCaster = false
viewRenderable.isShadowReceiver = true
selectionVisualizer.setFootprintRenderable(viewRenderable)
}
viewRenderable.isShadowCaster = false
Set this to true. More information here: https://developers.google.com/sceneform/reference/com/google/ar/sceneform/rendering/Renderable#isShadowCaster()
I followed the solution here for rotating a TransformableNode on the X axis based on the user's DragGesture, using the Sceneform Android SDK. However, I would also like to rotate on the Y and Z axis as well, similar to how ARCore SceneViewer does it.
How can I achieve that?
What I have currently is on the left (rotates only on X axis), and what is desired is on the right (rotates on all axes, as in ARCore Scene Viewer).
class DragRotationController(transformableNode: BaseTransformableNode, gestureRecognizer: DragGestureRecognizer) :
BaseTransformationController<DragGesture>(transformableNode, gestureRecognizer) {
// Rate that the node rotates in degrees per degree of twisting.
var rotationRateDegrees = 0.5f
public override fun canStartTransformation(gesture: DragGesture): Boolean {
return transformableNode.isSelected
}
public override fun onContinueTransformation(gesture: DragGesture) {
var localRotation = transformableNode.localRotation
val rotationAmountX = gesture.delta.x * rotationRateDegrees
val rotationDeltaX = Quaternion(Vector3.up(), rotationAmountX)
localRotation = Quaternion.multiply(localRotation, rotationDeltaX)
// *** this only rotates on X axis. How do I rotate on all axes? ***
transformableNode.localRotation = localRotation
}
public override fun onEndTransformation(gesture: DragGesture) {}
}
I was able to find a working solution here: https://github.com/chnouman/SceneView
Here are the relevant snippets of code, with some of my adaptations to make it work for .glb files.
I have forked this repo and am working on keyboard input support if anyone's interested in that.
Rendering the object:
private fun renderLocalObject() {
skuProgressBar.setVisibility(View.VISIBLE)
ModelRenderable.builder()
.setSource(this,
RenderableSource.builder().setSource(
this,
Uri.parse(localModel),
RenderableSource.SourceType.GLB)/*RenderableSource.SourceType.GLTF2)*/
.setScale(0.25f)
.setRecenterMode(RenderableSource.RecenterMode.ROOT)
.build())
.setRegistryId(localModel)
.build()
.thenAccept { modelRenderable: ModelRenderable ->
skuProgressBar.setVisibility(View.GONE)
addNodeToScene(modelRenderable)
}
Adding the object to the SceneView:
private fun addNodeToScene(model: ModelRenderable) {
if (sceneView != null) {
val transformationSystem = makeTransformationSystem()
var dragTransformableNode = DragTransformableNode(1f, transformationSystem)
dragTransformableNode?.renderable = model
sceneView.getScene().addChild(dragTransformableNode)
dragTransformableNode?.select()
sceneView.getScene()
.addOnPeekTouchListener { hitTestResult: HitTestResult?, motionEvent: MotionEvent? ->
transformationSystem.onTouch(
hitTestResult,
motionEvent
)
}
}
}
Custom TransformableNode:
class DragTransformableNode(val radius: Float, transformationSystem: TransformationSystem) :
TransformableNode(transformationSystem) {
val dragRotationController = DragRotationController(
this,
transformationSystem.dragRecognizer
)
}
Custom TransformationController:
class DragRotationController(
private val transformableNode: DragTransformableNode,
gestureRecognizer: DragGestureRecognizer
) :
BaseTransformationController<DragGesture>(transformableNode, gestureRecognizer) {
companion object {
private const val initialLat = 26.15444376319647
private const val initialLong = 18.995950736105442
var lat: Double = initialLat
var long: Double = initialLong
}
// Rate that the node rotates in degrees per degree of twisting.
private var rotationRateDegrees = 0.5f
public override fun canStartTransformation(gesture: DragGesture): Boolean {
return transformableNode.isSelected
}
private fun getX(lat: Double, long: Double): Float {
return (transformableNode.radius * Math.cos(Math.toRadians(lat)) * Math.sin(Math.toRadians(long))).toFloat()
}
private fun getY(lat: Double, long: Double): Float {
return transformableNode.radius * Math.sin(Math.toRadians(lat)).toFloat()
}
private fun getZ(lat: Double, long: Double): Float {
return (transformableNode.radius * Math.cos(Math.toRadians(lat)) * Math.cos(Math.toRadians(long))).toFloat()
}
override fun onActivated(node: Node?) {
super.onActivated(node)
Handler().postDelayed({
transformCamera(lat, long)
}, 0)
}
public override fun onContinueTransformation(gesture: DragGesture) {
val rotationAmountY = gesture.delta.y * rotationRateDegrees
val rotationAmountX = gesture.delta.x * rotationRateDegrees
val deltaAngleY = rotationAmountY.toDouble()
val deltaAngleX = rotationAmountX.toDouble()
long -= deltaAngleX
lat += deltaAngleY
//lat = Math.max(Math.min(lat, 90.0), 0.0)
transformCamera(lat, long)
}
private fun transformCamera(lat: Double, long: Double) {
val camera = transformableNode.scene?.camera
var rot = Quaternion.eulerAngles(Vector3(0F, 0F, 0F))
val pos = Vector3(getX(lat, long), getY(lat, long), getZ(lat, long))
rot = Quaternion.multiply(rot, Quaternion(Vector3.up(), (long).toFloat()))
rot = Quaternion.multiply(rot, Quaternion(Vector3.right(), (-lat).toFloat()))
camera?.localRotation = rot
camera?.localPosition = pos
}
fun resetInitialState() {
transformCamera(initialLat, initialLong)
}
public override fun onEndTransformation(gesture: DragGesture) {}
}
I have a nice map with icon markers and a recyclerview setup using Mapbox. It is similar to this:
https://docs.mapbox.com/android/maps/examples/recyclerview-interaction
My problem is that I can not figure out how to set the icons according to my local json property. I have in total around 40 different icons I need to use so I thought the best place to set that would be in my geojson properties. I have the png files for my icons in my drawable-xxhdpi folder. Here is what my local geojson file looks like:
{
"type": "Feature",
"properties": {
"name": "Broadway",
"icon": "route_icon_1",
"hours": "V1",
"phone": "One Star Rating",
"description": "Located at Lower Point Boulders"
},
"geometry": {
"type": "Point",
"coordinates": [
-86.311166,
33.919872
]
}
},
{
"type": "Feature",
"properties": {
"name": "Return of the Jedi",
"icon": "route_icon_2",
"hours": "V7",
"phone": "One Star Rating",
"description": "Located at Lower Point Boulders"
},
"geometry": {
"type": "Point",
"coordinates": [
-86.311174,
33.919883
]
}
},
I'm accessing the geojson file just fine and everything is running perfectly, but I can not seem to set my icon according to the "icon" property in the geojson file. Is this even possible? I apologize for the length of my code. This is my first time asking a question on here and I wasn't sure how much of the code I should post.
Here is my code in my map activity:
class MapActivity : AppCompatActivity(), OnMapReadyCallback, LocationRecyclerViewAdapter.ClickListener,
MapboxMap.OnMapClickListener {
private var featureCollection: FeatureCollection? = null
private var mapboxMap: MapboxMap? = null
private var mapView: MapView? = null
private var locationsRecyclerView: RecyclerView? = null
private var listOfIndividualLocations: ArrayList<IndividualLocation>? = null
private var styleRvAdapter: LocationRecyclerViewAdapter? = null
private var markerSelected = false
private var markerAnimator: ValueAnimator? = null
private var selectedMarkerIcon: Bitmap? = null
private var unselectedMarkerIcon: Bitmap? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Configure the Mapbox access token. Configuration can either be called in your application
// class or in the same activity which contains the mapview.
Mapbox.getInstance(this, getString(R.string.access_token))
// Hide the status bar for the map to fill the entire screen
#Suppress("DEPRECATION")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
window.insetsController?.hide(WindowInsets.Type.statusBars())
} else {
window.setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN
)
}
// Inflate the layout with the the MapView. Always inflate this after the Mapbox access token is configured.
setContentView(R.layout.activity_map)
// Create a GeoJSON feature collection from the GeoJSON file in the assets folder.
try {
featureCollectionFromJson
} catch (exception: Exception) {
Log.e("MapActivity", "onCreate: $exception")
Toast.makeText(this, R.string.failure_to_load_file, Toast.LENGTH_LONG).show()
}
// Initialize a list of IndividualLocation objects for future use with recyclerview
listOfIndividualLocations = ArrayList()
// Set up the Mapbox map
mapView = findViewById(R.id.mapView)
mapView?.onCreate(savedInstanceState)
mapView?.getMapAsync(this)
}
override fun onMapReady(mapboxMap: MapboxMap) {
this.mapboxMap = mapboxMap
// Initialize the custom class that handles marker icon creation and map styling based on the selected theme
selectedMarkerIcon = BitmapFactory.decodeResource(
resources,
R.drawable.basic_red_marker
)
unselectedMarkerIcon = BitmapFactory.decodeResource(
resources,
R.drawable.basic_red_marker
)
mapboxMap.setStyle(
Style.MAPBOX_STREETS
)
{ style ->
// Setting the returned mapboxMap object (directly above) equal to the "globally declared" one
this#MapActivity.mapboxMap = mapboxMap
mapboxMap.addOnMapClickListener(this)
val quad = LatLngQuad(
LatLng(33.92307338188975, -86.31129446659145),
LatLng(33.92307338188975, -86.30688718139171),
LatLng(33.91948714730282, -86.30688718139171),
LatLng(33.91948714730282, -86.31129446659145)
)
val quad2 = LatLngQuad(
LatLng(33.921356, -86.307309),
LatLng(33.921356, -86.307156),
LatLng(33.921200, -86.307156),
LatLng(33.921200, -86.307309)
)
// Add an ImageSource to the map
style.addSource(ImageSource(ID_IMAGE_SOURCE, quad, R.drawable.asset14))
style.addSource(ImageSource(ID_IMAGE_SOURCE_VANDALA, quad2, R.drawable.vandala_overlay_sm))
// Create a raster layer and use the imageSource's ID as the layer's data. Then add a RasterLayer to the map.
style.addLayerBelow(
RasterLayer(ID_IMAGE_LAYER, ID_IMAGE_SOURCE),
LAYER_ID
)
style.addLayerAbove(
RasterLayer(ID_IMAGE_LAYER_VANDALA, ID_IMAGE_SOURCE_VANDALA),
ID_IMAGE_LAYER
)
// Set bounds for the map camera so that the user can't pan the map outside of the NYC area
mapboxMap.setLatLngBoundsForCameraTarget(LOCKED_MAP_CAMERA_BOUNDS)
// Set up the SymbolLayer which will show the icons for each route location
initRouteLocationIconSymbolLayer()
// Set up the SymbolLayer which will show the selected route icon
initSelectedRouteSymbolLayer()
// Create a list of features from the feature collection
if (featureCollection != null) {
val featureList: List<Feature> = featureCollection?.features() as List<Feature>
// Retrieve and update the source designated for showing the route location icons
mapboxMap.style?.getSourceAs<GeoJsonSource?>(SOURCE_ID)!!
.setGeoJson(FeatureCollection.fromFeatures(featureList))
for (x in featureList.indices) {
val singleLocation: Feature = featureList[x]
// Get the single location's String properties to place in its map marker
val singleLocationName: String =
singleLocation.getStringProperty("name")
val singleLocationRouteIcon: String =
singleLocation.getStringProperty("icon")
Log.e("tag", singleLocationRouteIcon)
val singleLocationHours: String =
singleLocation.getStringProperty("hours")
val singleLocationDescription: String =
singleLocation.getStringProperty("description")
val singleLocationPhoneNum: String =
singleLocation.getStringProperty("phone")
// Add a boolean property to use for adjusting the icon of the selected route location
singleLocation.addBooleanProperty(PROPERTY_SELECTED, false)
// Get the single location's LatLng coordinates
val singleLocationPosition: Point = singleLocation.geometry() as Point
// Create a new LatLng object with the Position object created above
val singleLocationLatLng = LatLng(
singleLocationPosition.latitude(),
singleLocationPosition.longitude()
)
// Add the location to the Arraylist of locations for later use in the recyclerview
listOfIndividualLocations!!.add(
IndividualLocation(
singleLocationName,
singleLocationRouteIcon,
singleLocationDescription,
singleLocationHours,
singleLocationPhoneNum,
singleLocationLatLng
)
)
}
}
// the Maps SDK's LocationComponent can be used to easily display and customize
// the device location's puck
setUpRecyclerViewOfLocationCards()
Toast.makeText(this#MapActivity, "Click on a card", Toast.LENGTH_SHORT)
.show()
}
}
override fun onMapClick(point: LatLng): Boolean {
Log.e("tag", "onMapClick")
val style = mapboxMap!!.style
if (style != null) {
val selectedMarkerSymbolLayer = style.getLayer("selected-route-location-layer-id") as SymbolLayer?
val pixel = mapboxMap!!.projection.toScreenLocation(point)
val features = mapboxMap!!.queryRenderedFeatures(pixel, "route-location-layer-id")
val selectedFeature = mapboxMap!!.queryRenderedFeatures(
pixel, "selected-route-location-layer-id"
)
if (selectedFeature.size > 0 && markerSelected) {
Log.e(
"tag",
"1 onMapClick: selectedFeature.size > 0 && markerSelected ${selectedFeature.size} $markerSelected"
)
return false
}
if (features.isEmpty()) {
Log.e("tag", "2 onMapClick: features.isEmpty")
if (markerSelected) {
Log.e("tag", "selectMarker = true")
deselectMarker(selectedMarkerSymbolLayer!!)
}
return false
}
val source = style.getSourceAs<GeoJsonSource>(ID_SELECTED_ROUTE_ICON)
source?.setGeoJson(
FeatureCollection.fromFeatures(
arrayOf(
Feature.fromGeometry(
features[0].geometry()
)
)
)
)
if (markerSelected) {
Log.e("tag", "3 onMapClick: selectMarker")
deselectMarker(selectedMarkerSymbolLayer!!)
}
if (features.size > 0) {
Log.e("tag", "4 onMapClick: features.size > 0")
Log.e("tag", "4 onMapClick: features.size = ${features.size}")
selectMarker(selectedMarkerSymbolLayer!!)
}
}
handleClickIcon(mapboxMap?.projection!!.toScreenLocation(point))
return true
}
private fun handleClickIcon(screenPoint: PointF): Boolean {
Log.e("tag", "handleClickIcon")
val features: List<Feature> = mapboxMap?.queryRenderedFeatures(
screenPoint,
LAYER_ID,
"route-location-layer-id"
) as List<Feature>
return if (features.isNotEmpty()) {
Log.e("tag", "1 handleClickIcon: features.isNotEmpty")
val name: String = features[0].getStringProperty("name")
val featureList: List<Feature> = featureCollection?.features() as List<Feature>
for (i in featureList.indices) {
if (featureList[i].getStringProperty("name") == name) {
Log.e("tag", "(${featureList[i].getStringProperty("name")} == $name)")
val selectedFeaturePoint: Point = featureList[i].geometry() as Point
Log.e("tag", "handleClickIcon: featureSelectStatus = ${featureSelectStatus(i)}")
if (featureSelectStatus(i)) {
Log.e("tag", "${featureSelectStatus(i)}")
Log.e(
"tag", "2 handleClickIcon: featureSelectStatus = ${
featureSelectStatus(
i
)
}"
)
setFeatureSelectState(featureList[i], true)
Log.e(
"tag",
"2 handleClickIcon: setFeatureSelectState(featureList $i: selectedState set to false)"
)
} else {
Log.e(
"tag", "3 handleClickIcon: featureSelectStatus = ${
featureSelectStatus(
i
)
}"
)
setSelected(i)
}
if (selectedFeaturePoint.latitude() != MOCK_DEVICE_LOCATION_LAT_LNG.latitude) {
Log.e(
"tag",
"selectedFeaturePoint.latitude != MOCK_DEVICE_LOCATION_LAT_LNG.latitude"
)
for (x in 0 until featureCollection!!.features()!!.size) {
if (listOfIndividualLocations!![x].getLocation()?.latitude == selectedFeaturePoint.latitude()) {
// Scroll the recyclerview to the selected marker's card. It's "x-1" below because
// the mock device location marker is part of the marker list but doesn't have its own card
// in the actual recyclerview.
locationsRecyclerView!!.smoothScrollToPosition(x)
Log.e("tag", "smoothScrollToPosition")
}
}
}
}
else
{
setFeatureSelectState(featureList[i], false)
Log.e("tag", "4 handleClickIcon: (${featureList[i]}")
}
}
true
}
else
{
false
}
}
private fun selectMarker(iconLayer: SymbolLayer) {
Log.e("tag", "selectMarker: markerSelect set to true")
markerAnimator = ValueAnimator()
markerAnimator!!.setObjectValues(1f, 2f)
markerAnimator!!.duration = 300
markerAnimator!!.addUpdateListener { animator ->
iconLayer.setProperties(
iconSize(animator.animatedValue as Float)
)
}
markerAnimator!!.start()
markerSelected = true
}
private fun deselectMarker(iconLayer: SymbolLayer) {
Log.e("tag", "deselectMarker: markerSelect set to false")
markerAnimator!!.setObjectValues(1.5f, 1f)
markerAnimator!!.duration = 300
markerAnimator!!.addUpdateListener { animator ->
iconLayer.setProperties(
iconSize(animator.animatedValue as Float)
)
}
markerAnimator!!.start()
markerSelected = false
}
/**
* The LocationRecyclerViewAdapter's interface which listens to clicks on each location's card
*
* #param position the clicked card's position/index in the overall list of cards
*/
override fun onItemClick(position: Int) {
Log.e("tag", "itemClick")
// Get the selected individual location via its card's position in the recyclerview of cards
val selectedLocation: IndividualLocation = listOfIndividualLocations!![position]
// Evaluate each Feature's "select state" to appropriately style the location's icon
val featureList: List<Feature> = featureCollection?.features() as List<Feature>
val selectedLocationPoint: Point =
featureCollection!!.features()?.get(position)?.geometry() as Point
for (i in featureList.indices) {
if (featureList[i].getStringProperty("name") == selectedLocation.name) {
Log.e(
"tag",
"(${featureList[i].getStringProperty("name")} == ${selectedLocation.name})"
)
if (featureSelectStatus(i)) {
Log.e("tag", "1 featureSelectStatus = ${featureSelectStatus(i)}")
// setFeatureSelectState(featureList[i], false)
Toast.makeText(
applicationContext,
"Go to ${selectedLocation.name} Details Activity",
Toast.LENGTH_LONG
).show()
Log.e("tag", "Go to ${selectedLocation.name} Details Activity")
} else {
Log.e("tag", "2 itemClick: setSelected $i")
setSelected(i)
}
} else {
setFeatureSelectState(featureList[i], false)
Log.e("tag", "3 itemClick: featureList $i: selectedState false")
}
}
selectedLocation.getLocation()?.let { onCardClick(it) }
// Reposition the map camera target to the selected marker
repositionMapCamera(selectedLocationPoint)
}
private fun onCardClick(point: LatLng): Boolean {
Log.e("tag", "onCardClick")
val style = mapboxMap!!.style
if (style != null) {
val selectedMarkerSymbolLayer = style.getLayer("selected-route-location-layer-id") as SymbolLayer?
val pixel = mapboxMap!!.projection.toScreenLocation(point)
val features = mapboxMap!!.queryRenderedFeatures(pixel, "route-location-layer-id")
val selectedFeature = mapboxMap!!.queryRenderedFeatures(
pixel, "selected-route-location-layer-id"
)
if (selectedFeature.size > 0 && markerSelected) {
Log.e(
"tag",
"1 onCardClick: selectedFeature.size > 0 && markerSelected ${selectedFeature.size} $markerSelected"
)
return false
}
if (features.isEmpty()) {
Log.e("tag", "2 onCardClick: features.isEmpty")
if (markerSelected) {
Log.e("tag", "Do Nothing")
return false
}
else {
Log.e("tag", "markerSelected = $markerSelected")
selectMarker(selectedMarkerSymbolLayer!!)
}
return false
}
val source = style.getSourceAs<GeoJsonSource>(ID_SELECTED_ROUTE_ICON)
source?.setGeoJson(
FeatureCollection.fromFeatures(
arrayOf(
Feature.fromGeometry(
features[0].geometry()
)
)
)
)
if (markerSelected) {
Log.e("tag", "3 onCardClick: markerSelected")
deselectMarker(selectedMarkerSymbolLayer!!)
}
if (features.size > 0) {
Log.e("tag", "4 onCardClick: features.size > 0")
Log.e("tag", "4 onCardClick: features.size = ${features.size}")
selectMarker(selectedMarkerSymbolLayer!!)
}
}
return true
}
/**
* Adds a SymbolLayer which will show all of the location's icons
*/
private fun initRouteLocationIconSymbolLayer() {
val style: Style? = mapboxMap?.style
if (style != null) {
// Add the icon image to the map
style.addImage(
ID_ROUTE_ICON, unselectedMarkerIcon as Bitmap
)
style.transition = TransitionOptions(0, 0, false)
// Create and add the GeoJsonSource to the map
val routeLocationGeoJsonSource = GeoJsonSource(SOURCE_ID)
style.addSource(routeLocationGeoJsonSource)
// Create and add the route location icon SymbolLayer to the map
val routeLocationSymbolLayer = SymbolLayer(
"route-location-layer-id",
SOURCE_ID
)
routeLocationSymbolLayer.withProperties(
iconImage(ID_ROUTE_ICON),
iconAllowOverlap(true),
iconOffset(arrayOf(0f, -9f))
)
style.addLayer(routeLocationSymbolLayer)
} else {
Log.d("RouteFinderActivity", "initRouteLocationIconSymbolLayer: Style isn't ready yet.")
throw IllegalStateException("Style isn't ready yet.")
}
}
/**
* Adds a SymbolLayer which will show the selected location's icon
*/
private fun initSelectedRouteSymbolLayer() {
val style: Style? = mapboxMap?.style
if (style != null) {
// Add the icon image to the map
style.addImage(
ID_SELECTED_ROUTE_ICON, selectedMarkerIcon as Bitmap
)
// Create and add the route location icon SymbolLayer to the map
val selectedRouteLocationSymbolLayer = SymbolLayer(
"selected-route-location-layer-id",
SOURCE_ID
)
selectedRouteLocationSymbolLayer.withProperties(
iconImage(ID_SELECTED_ROUTE_ICON),
iconOffset(arrayOf(0f, -9f)),
iconAllowOverlap(true)
)
selectedRouteLocationSymbolLayer.withFilter(eq(get(PROPERTY_SELECTED), literal(true)))
style.addLayer(selectedRouteLocationSymbolLayer)
} else {
Log.d("RouteFinderActivity", "initSelectedRouteSymbolLayer: Style isn't ready yet.")
throw IllegalStateException("Style isn't ready yet.")
}
}
/**
* Checks whether a Feature's boolean "selected" property is true or false
*
* #param index the specific Feature's index position in the FeatureCollection's list of Features.
* #return true if "selected" is true. False if the boolean property is false.
*/
private fun featureSelectStatus(index: Int): Boolean {
return if (featureCollection == null) {
false
} else featureCollection!!.features()?.get(index)?.getBooleanProperty(PROPERTY_SELECTED)!!
}
/**
* Set a feature selected state.
*
* #param index the index of selected feature
*/
private fun setSelected(index: Int) {
Log.e("tag", "setSelected")
val feature: Feature = featureCollection?.features()!![index]
setFeatureSelectState(feature, true)
Log.e(
"tag",
"setSelected: setFeatureSelectedState(feature = ${featureCollection?.features()!![index]}, selectedState set to true"
)
refreshSource()
}
/**
* Selects the state of a feature
*
* #param feature the feature to be selected.
*/
private fun setFeatureSelectState(feature: Feature, selectedState: Boolean) {
feature.properties()?.addProperty(PROPERTY_SELECTED, selectedState)
refreshSource()
}
/**
* Updates the display of data on the map after the FeatureCollection has been modified
*/
private fun refreshSource() {
val source: GeoJsonSource? = mapboxMap?.style?.getSourceAs(SOURCE_ID)
if (source != null && featureCollection != null) {
source.setGeoJson(featureCollection)
}
}
private fun repositionMapCamera(newTarget: Point) {
val newCameraPosition: CameraPosition = CameraPosition.Builder()
.target(LatLng(newTarget.latitude(), newTarget.longitude()))
.zoom(22.0)
.build()
mapboxMap?.animateCamera(
CameraUpdateFactory.newCameraPosition(newCameraPosition),
CAMERA_MOVEMENT_SPEED_IN_MILSECS
)
}
// Use fromJson() method to convert the GeoJSON file into a usable FeatureCollection object
#get:Throws(IOException::class)
private val featureCollectionFromJson: Unit
get() {
try {
// Use fromJson() method to convert the GeoJSON file into a usable FeatureCollection object
featureCollection = "list_of_locations.geojson".loadGeoJsonFromAsset()?.let {
FeatureCollection.fromJson(
it
)
}
} catch (exception: Exception) {
Log.e("MapActivity", "getFeatureCollectionFromJson: $exception")
}
}
private fun String.loadGeoJsonFromAsset(): String? {
Log.e("tag", "loadGeoJsonFromAsset")
return try {
// Load the GeoJSON file from the local asset folder
val `is`: InputStream = assets.open(this)
val size: Int = `is`.available()
val buffer = ByteArray(size)
`is`.read(buffer)
`is`.close()
String(buffer, charset("UTF-8"))
} catch (exception: Exception) {
Log.e("MapActivity", "Exception Loading GeoJSON: $exception")
exception.printStackTrace()
null
}
}
private fun setUpRecyclerViewOfLocationCards() {
Log.e("tag", "seUpRecyclerViewOfLocationCards")
// Initialize the recyclerview of location cards and a custom class for automatic card scrolling
locationsRecyclerView = findViewById(R.id.map_layout_rv)
locationsRecyclerView?.setHasFixedSize(true)
locationsRecyclerView?.layoutManager = LinearLayoutManagerWithSmoothScroller(this)
styleRvAdapter = listOfIndividualLocations?.let {
LocationRecyclerViewAdapter(
it,
applicationContext, this
)
}
locationsRecyclerView?.adapter = styleRvAdapter
val snapHelper: SnapHelper = LinearSnapHelper()
snapHelper.attachToRecyclerView(locationsRecyclerView)
}
// Add the mapView's lifecycle to the activity's lifecycle methods
public override fun onResume() {
super.onResume()
mapView?.onResume()
}
override fun onStart() {
super.onStart()
mapView?.onStart()
}
override fun onStop() {
super.onStop()
mapView?.onStop()
}
public override fun onPause() {
super.onPause()
mapView?.onPause()
}
override fun onLowMemory() {
super.onLowMemory()
mapView?.onLowMemory()
}
override fun onDestroy() {
super.onDestroy()
mapView?.onDestroy()
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
mapView?.onSaveInstanceState(outState)
}
companion object {
private val LOCKED_MAP_CAMERA_BOUNDS = LatLngBounds.Builder()
.include(LatLng(33.92307338188975, -86.30688718139171))
.include(
LatLng(
33.91948714730282,
-86.31129446659145
)
).build()
private val MOCK_DEVICE_LOCATION_LAT_LNG: LatLng =
LatLng(33.921466408978034, -86.30824978533974)
private const val CAMERA_MOVEMENT_SPEED_IN_MILSECS = 1200
private const val PROPERTY_SELECTED = "selected"
private const val ID_ROUTE_ICON = "route-icon-id"
private const val ID_SELECTED_ROUTE_ICON = "selected-route-icon-id"
private const val LAYER_ID = "LAYER_ID"
private const val ID_IMAGE_SOURCE = "image_source-id"
private const val ID_IMAGE_LAYER = "image_layer-id"
private const val ID_IMAGE_SOURCE_VANDALA = "image_source-id_vandala"
private const val ID_IMAGE_LAYER_VANDALA = "image_layer-id_vandala"
private const val SOURCE_ID = "route-location-source-id"
}
}
Thank you in advance.
You are almost there. You need a bit of code more.
On your function initRouteLocationIconSymbolLayer(), you have the following code:
routeLocationSymbolLayer.withProperties(
iconImage(ID_ROUTE_ICON), // <---- Here is where you have to change
iconAllowOverlap(true),
iconOffset(arrayOf(0f, -9f))
)
You have only set your layers to have one icon. The MapBoxSDK allows you to play with the properties of the geojson file.
So, here we need to change how the icon image is generated for each type of icon
routeLocationSymbolLayer
.withProperties(
/* iconImage constructor, with some query-like code */
iconImage(
match(
get("icon"), // JSON Key to evaluate
literal("route_icon_1"), // default
stop("route_icon_1", "route_icon_1"), // stop(input,output)
stop("route_icon_2", "route_icon_2")
)
),
/*the remaining properties you declared*/
iconAllowOverlap(true),
iconOffset(arrayOf(0f, -9f)
)
Source: MapBox Android SDK Example
Here is the code:
private fun setData(playerData: TreeMap<String, String>) {
val values = ArrayList<Entry>()
val graphData = java.util.ArrayList<Float>()
for (value in playerData.values) {
graphData.add(value.toInt().toFloat()) # <-- converting here
}
graphData.reverse()
for (i in 0 until graphData.size) {
val itemi = i.toFloat()
values.add(
Entry(
itemi, #<-- entering here directly
graphData[i],
resources.getDrawable(R.drawable.ic_cricket)
)
)
}
val set1: LineDataSet
set1 = LineDataSet(values, "Player Form Graph")
val dataSets = ArrayList<ILineDataSet>()
// Possible Solution?
val valueFormatter = IValueFormatter { value, _, _, _ ->
value.toInt().toString()
}
set1.valueFormatter = valueFormatter as ValueFormatter?
dataSets.add(set1)
val data = LineData(dataSets)
// set data
binding.chart1.data = data
}
Error: java.lang.ClassCastException: <id>DemoFragment$setData$valueFormatter$1 cannot be cast to com.github.mikephil.charting.formatter.ValueFormatter
The thing is some values are working without .0 and some are with .0
this is what I have used:
set1.valueFormatter = object : ValueFormatter() {
override fun getFormattedValue(value: Float): String {
return value.toInt().toString()
}
}
In case if there is a chance of value coming in decimal as well, say [1, 1.2, 3, 3.5] is your set, then go for:
set1.valueFormatter = object : ValueFormatter() {
override fun getFormattedValue(value: Float): String {
return DecimalFormat("#.#").format(value)
}
}
in above case #.# can be replaced with #.## if you want to show UPTO 2 decimal places and so on.
Use:
DecimalFormat df = new DecimalFormat( "0" );