Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update show coordinates sample #152

Merged
merged 13 commits into from
Dec 18, 2023
6 changes: 5 additions & 1 deletion show-coordinates-in-multiple-formats/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ Tap on the map to see a marker with the tapped location's coordinate formatted i
* CoordinateFormatter.LatitudeLongitudeFormat
* CoordinateFormatter.UtmConversionMode

## Additional information

This sample uses the GeoCompose Toolkit module to be able to implement a Composable MapView.

## Tags

convert, coordinate, decimal degrees, degree minutes seconds, format, latitude, longitude, USNG, UTM
convert, coordinate, decimal degrees, degree minutes seconds, format, geocompose, latitude, longitude, toolkit, USNG, UTM
3 changes: 2 additions & 1 deletion show-coordinates-in-multiple-formats/README.metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
"decimal degrees",
"degree minutes seconds",
"format",
"geocompose",
"latitude",
"longitude",
"toolkit",
"CoordinateFormatter",
"CoordinateFormatter.LatitudeLongitudeFormat",
"CoordinateFormatter.UtmConversionMode"
Expand All @@ -29,7 +31,6 @@
],
"snippets": [
"src/main/java/com/esri/arcgismaps/sample/showcoordinatesinmultipleformats/MainActivity.kt",
"src/main/java/com/esri/arcgismaps/sample/showcoordinatesinmultipleformats/components/ComposeMapView.kt",
"src/main/java/com/esri/arcgismaps/sample/showcoordinatesinmultipleformats/components/MapViewModel.kt",
"src/main/java/com/esri/arcgismaps/sample/showcoordinatesinmultipleformats/screens/CoordinatesLayout.kt",
"src/main/java/com/esri/arcgismaps/sample/showcoordinatesinmultipleformats/screens/MainScreen.kt"
Expand Down
3 changes: 3 additions & 0 deletions show-coordinates-in-multiple-formats/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,7 @@ dependencies {
implementation "androidx.compose.ui:ui-tooling"
implementation "androidx.compose.ui:ui-tooling-preview"
implementation project(path: ':samples-lib')
// Toolkit dependencies
implementation(platform("com.esri:arcgis-maps-kotlin-toolkit-bom:$arcgisToolkitVersion"))
implementation('com.esri:arcgis-maps-kotlin-toolkit-geo-compose')
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.esri.arcgismaps.sample.showcoordinatesinmultipleformats

import android.app.Application
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
Expand Down Expand Up @@ -48,7 +49,9 @@ class MainActivity : ComponentActivity() {
@Composable
private fun ShowCoordinatesInMultipleFormatsApp() {
Surface(color = MaterialTheme.colorScheme.background) {
MainScreen(sampleName = getString(R.string.app_name))
MainScreen(sampleName = getString(R.string.app_name),
application = application
)
}
}
}

This file was deleted.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The graphic point does not update when I try the reverse workflow.

  • Tapping a point elsewhere
  • Pasting in a predeterminate coordinate
  • All text fields update to the new coordinate as expected
  • The graphic point on map does not

Could you debug to see if this issue is due to the toolkit MapView or the sample workflow?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch, fixed...
the geometry of the coordinateLocationGraphic was not getting updated to the new Point.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The app crashes when I type in an incorrect coordinate point. Ideally, it should display the messageDialogVM.showMessageDialog

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see above comment..

Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,13 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.AndroidViewModel
import com.arcgismaps.Color
import com.arcgismaps.geometry.CoordinateFormatter
import com.arcgismaps.geometry.LatitudeLongitudeFormat
import com.arcgismaps.geometry.Point
import com.arcgismaps.geometry.SpatialReference
import com.arcgismaps.geometry.UtmConversionMode
import com.arcgismaps.mapping.ArcGISMap
import com.arcgismaps.mapping.Basemap
import com.arcgismaps.mapping.BasemapStyle
import com.arcgismaps.mapping.layers.ArcGISTiledLayer
import com.arcgismaps.mapping.symbology.SimpleMarkerSymbol
import com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyle
import com.arcgismaps.mapping.view.Graphic
import com.arcgismaps.mapping.view.GraphicsOverlay
import com.esri.arcgismaps.sample.sampleslib.components.MessageDialogViewModel
import com.esri.arcgismaps.sample.showcoordinatesinmultipleformats.R

class MapViewModel(application: Application) : AndroidViewModel(application) {
// set the MapView state
val mapViewState = MapViewState()

var decimalDegrees by mutableStateOf("")
private set
Expand All @@ -53,54 +40,15 @@ class MapViewModel(application: Application) : AndroidViewModel(application) {
var usng by mutableStateOf("")
private set

// set up a graphic to indicate where the coordinates relate to, with an initial location
private val initialPoint = Point(0.0, 0.0, SpatialReference.wgs84())

private val coordinateLocation = Graphic(
geometry = initialPoint,
symbol = SimpleMarkerSymbol(
style = SimpleMarkerSymbolStyle.Cross,
color = Color.fromRgba(255, 255, 0, 255),
size = 20f
)
)

// create a ViewModel to handle dialog interactions
val messageDialogVM: MessageDialogViewModel = MessageDialogViewModel()

init {
// create a map that has the WGS 84 coordinate system and set this into the map
val basemapLayer = ArcGISTiledLayer(application.getString(R.string.basemap_url))
val map = ArcGISMap(Basemap(basemapLayer))
mapViewState.arcGISMap = map
mapViewState.graphicsOverlay.graphics.add(coordinateLocation)

// update the coordinate notations using the initial point
toCoordinateNotationFromPoint(initialPoint)
}

/**
* Updates the tapped graphic and coordinate notations using the [tappedPoint]
*/
fun onMapTapped(tappedPoint: Point?) {
if (tappedPoint != null) {
// update the tapped location graphic
coordinateLocation.geometry = tappedPoint
mapViewState.graphicsOverlay.graphics.apply {
clear()
add(coordinateLocation)
}
// update the coordinate notations using the tapped point
toCoordinateNotationFromPoint(tappedPoint)
}
}

/**
* Uses CoordinateFormatter to update the UI with coordinate notation strings based on the
* given [newLocation] point to convert to coordinate notations
*/
private fun toCoordinateNotationFromPoint(newLocation: Point) {
coordinateLocation.geometry = newLocation
fun toCoordinateNotationFromPoint(newLocation: Point) {

// use CoordinateFormatter to convert to Latitude Longitude, formatted as Decimal Degrees
decimalDegrees = CoordinateFormatter.toLatitudeLongitudeOrNull(
point = newLocation,
Expand Down Expand Up @@ -206,11 +154,3 @@ class MapViewModel(application: Application) : AndroidViewModel(application) {
usng = inputString
}
}

/**
* Class that represents the MapView's current state
*/
class MapViewState {
var arcGISMap: ArcGISMap by mutableStateOf(ArcGISMap(BasemapStyle.ArcGISNavigationNight))
var graphicsOverlay: GraphicsOverlay by mutableStateOf(GraphicsOverlay())
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,40 +16,93 @@

package com.esri.arcgismaps.sample.showcoordinatesinmultipleformats.screens

import android.app.Application
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.lifecycle.viewmodel.compose.viewModel
import com.arcgismaps.Color
import com.arcgismaps.geometry.Point
import com.arcgismaps.geometry.SpatialReference
import com.arcgismaps.mapping.ArcGISMap
import com.arcgismaps.mapping.Basemap
import com.arcgismaps.mapping.layers.ArcGISTiledLayer
import com.arcgismaps.mapping.symbology.SimpleMarkerSymbol
import com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyle
import com.arcgismaps.mapping.view.Graphic
import com.arcgismaps.mapping.view.GraphicsOverlay
import com.arcgismaps.toolkit.geocompose.MapView
import com.arcgismaps.toolkit.geocompose.rememberGraphicsOverlayCollection
import com.esri.arcgismaps.sample.sampleslib.components.MessageDialog
import com.esri.arcgismaps.sample.sampleslib.components.SampleTopAppBar
import com.esri.arcgismaps.sample.showcoordinatesinmultipleformats.components.ComposeMapView
import com.esri.arcgismaps.sample.showcoordinatesinmultipleformats.R
import com.esri.arcgismaps.sample.showcoordinatesinmultipleformats.components.MapViewModel

/**
* Main screen layout for the sample app
*/
@Composable
fun MainScreen(sampleName: String) {
fun MainScreen(sampleName: String, application: Application) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be passed in? Could you use LocalContext.current.applicationContext?

// create a ViewModel to handle MapView interactions
val mapViewModel: MapViewModel = viewModel()
// create a map that has the WGS 84 coordinate system and set this into the map
val basemapLayer = ArcGISTiledLayer(application.getString(R.string.basemap_url))
val arcGISMap = ArcGISMap(Basemap(basemapLayer))
// the collection of graphics overlays used by the MapView
val graphicsOverlayCollection = rememberGraphicsOverlayCollection()
// the collection of graphics overlays used by the MapView
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a duplicate comment to line 55

val graphicsOverlay = remember { GraphicsOverlay() }
// set up a graphic to indicate where the coordinates relate to, with an initial location
val initialPoint = Point(0.0, 0.0, SpatialReference.wgs84())

val coordinateLocation = Graphic(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

coordinateLocation might be confused with an actual location point.

Could rename it to coordinateLocationGraphic

Suggested change
val coordinateLocation = Graphic(
val coordinateLocationGraphic = Graphic(

geometry = initialPoint,
symbol = SimpleMarkerSymbol(
style = SimpleMarkerSymbolStyle.Cross,
color = Color.fromRgba(255, 255, 0, 255),
size = 20f
)
)
graphicsOverlay.graphics.add(coordinateLocation)
// update the coordinate notations using the initial point
coordinateLocation.geometry = initialPoint
mapViewModel.toCoordinateNotationFromPoint(initialPoint)

Scaffold(
topBar = { SampleTopAppBar(title = sampleName) },
content = {
content = { it ->
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this intentional? I expect it to be named or not declared explicitly

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed it, thanks!

Column(
modifier = Modifier.fillMaxSize().padding(it)
modifier = Modifier
.fillMaxSize()
.padding(it)
) {
// layout to display the coordinate text fields.
CoordinatesLayout(mapViewModel = mapViewModel)
// composable function that wraps the MapView
ComposeMapView(
MapView(
modifier = Modifier.fillMaxSize(),
mapViewModel = mapViewModel,
onSingleTap = { singleTapConfirmedEvent ->
mapViewModel.onMapTapped(singleTapConfirmedEvent.mapPoint)
arcGISMap = arcGISMap,
graphicsOverlays = graphicsOverlayCollection.apply {
this.add(graphicsOverlay)
},
Copy link
Collaborator

@shubham7109 shubham7109 Dec 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think simple assignment properties here would be more clear, and we can move the apply block away from the Compose call and add it to the line the val is declared.

    // graphics overlay for the MapView to draw the graphics
    val graphicsOverlay = remember { GraphicsOverlay() }
    // the collection of graphics overlays used by the MapView
    val graphicsOverlayCollection = rememberGraphicsOverlayCollection().apply {
        add(graphicsOverlay)
    }
Suggested change
graphicsOverlays = graphicsOverlayCollection.apply {
this.add(graphicsOverlay)
},
graphicsOverlays = graphicsOverlayCollection,

Copy link
Author

@prupani-7 prupani-7 Dec 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This solution fixed the issue with showMessageDialog not showing and app crashing with error (GO object already owned)
I think everytime MapView composable was called, we were adding the same GO object to the GO collection, without clearing it.
but moving the apply block in declaration fixes this. Thanks!

onSingleTapConfirmed = { singleTapConfirmedEvent ->
/**
* Updates the tapped graphic and coordinate notations using the [tappedPoint]
*/
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/**
* Updates the tapped graphic and coordinate notations using the [tappedPoint]
*/
// retrieve the map point on MapView tapped

val tappedPoint = singleTapConfirmedEvent.mapPoint
if (tappedPoint != null) {
// update the tapped location graphic
coordinateLocation.geometry = tappedPoint
graphicsOverlay.graphics.apply {
clear()
add(coordinateLocation)
}
// update the coordinate notations using the tapped point
mapViewModel.toCoordinateNotationFromPoint(tappedPoint)
}
}
)

Expand Down
Loading