Интерфейс программы «Track tracking»

Отчёт по курсовой работе

по дисциплине

«Разработка информационно - управляющих систем»

на тему:

«Разработка приложения для Android “Track tracking”»

ПОЯСНИТЕЛЬНАЯ ЗАПИСКА

 

Студент группы ПИМ-19                                                          Головкин Д. М.

Студент группы ПИМ-19                                                           Соколов Д. С.

Руководитель                                                                                    Ломанов А.Н.

Рыбинск 2020



Содержание

 

1   Постановка задачи. 3

2   Используемые инструменты и технологии. 4

3   Назначение приложения и функциональные требования. 5

4   Этапы реализации. 6

5   Заключение. 8

6   Литература. 9

Приложения. 10

Приложение А.. 10

Приложение Б. 16

 

 



Постановка задачи

 

 

Целью курсовой работы является написание программы, способной обрабатывать потом данных и на его основе давать результат. Входными данным являются GPS координаты устройства, с которого выполняется программа. Данные, приходят на смартфон периодически, что позволяет на их основе строить карту маршрутов и определять местонахождение пользователя.

 



Используемые инструменты и технологии

 

 

В качестве языка разработки программного продукта, был выбран Kotlin, так как он предоставляет все необходимые компоненты и библиотеки для создания и стабильной работы в Android.

Kotlin – статически типизированный, объектно-ориентированный язык программирования, работающий поверх Java Virtual Machine и разрабатываемый компанией JetBrains. Благодаря его преимуществам, он был выбран приоритетным языком для создания Android-приложения компанией Google. Вот некоторые из плюсов языка:

1) Простые функции и структуры можно объявить одной строкой.

2) Геттеры и сеттеры задаются за кулисами для интероперабельности с Java-кодом.

3)  Kotlin не допускает возникновения NullPointerException, выдавая ошибку компиляции.

4) В Kotlin содержатся специальные классы, предназначенные специально для хранения данных. Они генерируют различные шаблоны: equals(), hashCode(), toString(), геттеры и сеттеры и т.д.

5) Так же Kotlin позволяет расширять функциональность существующих классов, не прибегая к наследованию. Это делается при помощи функций-расширений. Для объявления такой функции к её имени нужно приписать префикс в виде расширяемого типа.

6) Kotlin — это следующий этап развития Java, с которой он полностью совместим. Это делает его отличным инструментом для мобильных и энтерпрайз-приложений.

 

 



Назначение приложения и функциональные требования

 

 

Мобильные устройства часто используются для решения различных задач связанных с определением географических координат. Транспорт, строительство, путешественники, так или иначе, нуждаются в определении своего местоположения или других объектов. Реализация данного приложения поможет людям избегать плутания на местности и возвращаться в точку начала движения.

 

Функциональные требования:

· Определения сторон света;

· Отображение компаса на экране устройства;

· Отображение карты на экране устройства;

· Установка на карте отметок пройденного пути;

· Сбор и отображение статистики.


 


Этапы реализации

 

Задачу реализации программы можно разбить на 3 этапа:

1. получение GPS координат и их обработка;

2. симуляция компаса на экране;

3. отображение данных на карте.

 

Приложение отслеживает пройденный путь по GPS координатам, а также имеет компас для более простой ориентировки на местности, если GPS в этом месте не работает. Координаты по получаем каждые 20 секунд. Если координаты новые, то устанавливается маркер на карте, и эта координата запоминается. В меню статистика можно узнать пройденный путь за сессию работы приложения. При выключении все данные записываются в файл. И это позволяет вести учет данных за – неделю, месяц. В глобальной статистике доступна информация по пройденному километражу.

 

1. Получение GPS координат и их обработка:

 

В Android SDK весь функционал по работе с навигационными системами объединён в пакет android.location. Ключевые компоненты данного пакета:

· LocationManager – (класс) обеспечивает доступ к системной службе определения местоположения Android;

· LocationListener — (интерфейс) регламентирует обработку приложение событий службы определения местоположения Android;

· Location – (класс) представляет географические координаты полученные от навигационной системы.

Первым делом необходимо предоставить приложению необходимые разрешения в файле манифеста. Определяем поставщика данных о местоположении. В данном случае используется GPS. Определяется минимальный интервал обновления данных о местоположения в миллисекундах (Значение «0» соответствует использованию минимально возможного интервала времени для данного устройства). Определяется м инимальное расстояние для обновления данных о местоположении в метрах ( Значение «0» соответствует использованию минимально возможного расстояния для данного устройства). Полученные координаты за 10 метров или за 20 секунд будут добавлены на карту.

2. Симуляция компаса на экране:

В ситуациях, когда, по каким бы то ни было причинам, не удается получить данные при помощи спутников GPS, остается возможность использования компаса, отображаемого рядом с картой. Его функционирование производится за счет данных, получаемых с магнитометра и акселерометра. Магнитометр – это прибор для изменения характеристик магнитного поля, данный сенсор реагирует на магнитное поле земли, благодаря чему позволяет определять стороны света. Акселерометр – это датчик, который определяет отношение наклона телефона к земле. Акселерометр используется для измерения ускорения. Его иногда называют датчиком силы притяжения.

Акселерометры часто выступают в качестве датчиков силы притяжения, так как они не могут определить, чем вызвано ускорение — движением или гравитацией. В результате этого в состоянии покоя акселерометр будет указывать на ускорение по оси Z (вверх/вниз), равное 9,8м/с2 (это значение доступно в виде константы SensorManager.STANDARD_GRAVITY).

Ускорение — это производная скорости по времени, поэтому акселерометр определяет, насколько быстро изменяется скорость устройства в заданном направлении. Используя этот датчик, вы можете обнаруживать движение и, что более полезно, изменение его скорости. Акселерометр не измеряет скорость как таковую, поэтому вы не можете получить скорость движения, основываясь на единичном замере. Вместо этого необходимо учитывать изменения ускорения на протяжении какого-то отрезка времени.

Используя и обрабатывая данные с этих датчик, мы получаем возможность определения сторон света вне зависимости от положения телефона.

3. Отображение данных на карте:

 

Полученные и обработанные координаты отмечаются на карте устройства. Так пользователь сможет определить свой путь, тем самым вернувшись в исходную точку не заблудившись.

 



Заключение

 

 

В ходе выполнения курсовой работы был получен опыт в обработке большого потока данных поступающих на устройство. Обработка GPS координат (вычисление средних значений, полученных за определенный интервал времени) и отображение их на Google карте. Также получен опыт в разработке программ под устройства Android с помощью языка программирования Kotlin.



Литература

 

 

1. STARTANDROID. [Электронный ресурс] URL: https://startandroid.ru/ru/uroki/vse-uroki-spiskom/132-urok-69-peredaem-parcelable-obekty-s-pomoschju-intent.

2. Хабр. Пишем на Kotlin под Android. [Электронный ресурс] URL: https://habr.com/ru/company/JetBrains/blog/231525/

3. Developers. Location. [Электронный ресурс] URL: https://developer.android.com/reference/kotlin/android/location/Location 4. Официальное руководство по Kotlin. [Электронный ресурс] URL: https://kotlinlang.org/.



Приложения

Приложение А

Примеры программного кода

 

Класс MainActivity:

 

class MainActivity: AppCompatActivity(), LocationListener, SensorEventListener {

private val REQUEST_LOCATION = 2
private var sensorManager: SensorManager? = null
private var list_GPS = ArrayList<LatLng>()

/***************/
lateinit var mapFragment: SupportMapFragment
lateinit var googleMap: GoogleMap
/***************/

private var fileName = "statistics"
lateinit var file: File


override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   setContentView(R.layout.main_activity)
   setLocation()
   sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
   startCompass()
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
      menuInflater.inflate(R.menu.menu_main, menu)
   return true
}

//вызов StaticActivity
override fun onOptionsItemSelected(item: MenuItem): Boolean {
   return when (item.itemId) {
       R.id.action_settings -> {
           Toast.makeText(applicationContext, "статистика", Toast.LENGTH_LONG).show()
            val intent = Intent(this, StatisticActivity::class.java)
           intent.putExtra("list", list_GPS)
           startActivity(intent)
           true
       }
       else -> super.onOptionsItemSelected(item)
   }
}



private fun setLocation() {
if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED
     && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED){
     ActivityCompat.requestPermissions(this, arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_COARSE_LOCATION),
    REQUEST_LOCATION)
}else{

     var locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
     val criteria = Criteria()
     val provider = locationManager.getProviders(criteria, false) //locationManager.getBestProvider(criteria, false)
     val location = locationManager.getLastKnownLocation(provider[0])

     if (location!= null) {
         text_view_location.text = convertLocationToString(location.latitude, location.longitude)
         /***************/
        
if ((list_GPS.size==0) || (list_GPS[list_GPS.size-1]!= LatLng(location.latitude, location.longitude))){
             list_GPS.add(LatLng(location.latitude, location.longitude))
             mapFragment = supportFragmentManager.findFragmentById(R.id.mapView) as SupportMapFragment
             mapFragment.getMapAsync(OnMapReadyCallback {
                     
googleMap = it
                 val location1 = LatLng(location.latitude, location.longitude)
                 googleMap.addMarker(MarkerOptions().position(location1).title("Точка - " + list_GPS.size.toString()))
                 googleMap.addPolyline(PolylineOptions()
                    .addAll(list_GPS)
                    .width(5f)
                    .color(Color.RED))
                 googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(location1, 17f))
             })

             locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,20000,0f, this)

             if (list_GPS.size > 1){
                 var loc1 = Location("")
                 loc1.longitude = list_GPS.get(list_GPS.size - 2).longitude
                 loc1.latitude = list_GPS.get(list_GPS.size - 2).longitude

                 var loc2 = Location("")
                 loc2.longitude = list_GPS.get(list_GPS.size-1).longitude
                 loc2.latitude = list_GPS.get(list_GPS.size-1).longitude
                 var distanceInMeters = loc1.distanceTo(loc2)

                 var stringDate: String = ""


                 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                     var currentTime = LocalDateTime.now()
                     var formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy. HH:mm:ss")
                     stringDate = currentTime.format(formatter)
                 } else {
                     var date = Date()
                     var formatter = SimpleDateFormat("MMMM dd yyyy HH:mma")
                     stringDate = formatter.format(date)
                 }

                 var result = distanceInMeters.toString() + " " + stringDate
                 this.openFileOutput(fileName, Context.MODE_PRIVATE).write(result.toByteArray())

             }
         }


         /***************/
    
}else{
           Toast.makeText(this,"Локация не найдена!",Toast.LENGTH_SHORT).show()
     }
}
}

override fun onRequestPermissionsResult(
  requestCode: Int,
   permissions: Array<out String>,
   grantResults: IntArray
) {
   if(requestCode == REQUEST_LOCATION) setLocation()
}

private fun convertLocationToString(latitude: Double, longitude: Double): String {

    val builder = StringBuilder()
   if (latitude < 0) builder.append("S ") else builder.append("N ")

   val latitudeDegrees = Location.convert(Math.abs(latitude), Location.FORMAT_SECONDS)
   val latitudeSplit = latitudeDegrees.split((":").toRegex()).dropLastWhile({ it.isEmpty() }).toTypedArray()
   builder.append(latitudeSplit[0])
   builder.append("*")
   builder.append(latitudeSplit[1])
   builder.append("'")
   builder.append(latitudeSplit[2])
   builder.append("\"")
   builder.append("\n")

   if (longitude < 0) builder.append("W ") else builder.append("E ")
   val longlatitudeDegrees = Location.convert(Math.abs(longitude),Location.FORMAT_SECONDS)
   val longitudeSplit = longlatitudeDegrees.split((":")).dropLastWhile({ it.isEmpty() }).toTypedArray()
   builder.append(longitudeSplit[0])
   builder.append("*")
   builder.append(longitudeSplit[1])
   builder.append("'")
   builder.append(longitudeSplit[2])
   builder.append("\"")
   return builder.toString()
}

override fun onLocationChanged(location: Location?) {
   setLocation()
}

override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {

}

override fun onProviderEnabled(provider: String?) {

}

override fun onProviderDisabled(provider: String?) {

}

override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {

}

private var rotationMatrix = FloatArray(9)
private var orientation = FloatArray(3)
private var azimuth:Int = 0
private var lastAccelerometer = FloatArray(3)
private var lastAccelerometerSet = false
private var lastMagnetometer = FloatArray(3)
private var lastMagnetometerSet = false

   override fun onSensorChanged(event: SensorEvent?) {
       if (event!!.sensor.type == Sensor.TYPE_ROTATION_VECTOR){
           SensorManager.getRotationMatrixFromVector(rotationMatrix, event!!.values)
           azimuth = (Math.toDegrees(SensorManager.getOrientation(rotationMatrix, orientation)[0].toDouble())+360).toInt()%360
       }

       if (event!!.sensor.type == Sensor.TYPE_ACCELEROMETER){
           System.arraycopy(event!!.values, 0, lastAccelerometer,0,event.values.size)
           lastAccelerometerSet = true
       }else if(event.sensor.type == Sensor.TYPE_MAGNETIC_FIELD){
           System.arraycopy(event.values,0, lastMagnetometer,0,event.values.size)
           lastMagnetometerSet = true
       }
       if (lastAccelerometerSet && lastMagnetometerSet) {
           SensorManager.getRotationMatrix(rotationMatrix,null,lastAccelerometer, lastMagnetometer)
           SensorManager.getOrientation(rotationMatrix, orientation)
           azimuth = (Math.toDegrees(SensorManager.getOrientation(rotationMatrix,orientation)[0].toDouble())+360).toInt()%360
       }
       azimuth = Math.round(azimuth.toFloat())

       compass_image.rotation = (-azimuth).toFloat()

       val where = when(azimuth){
           in 281..349 -> "NW";
           in 261..280 -> "W"
           in 191..260 -> "SW"
           in 171..190 -> "S"
           in 101..170 -> "SE"
           in 81..100 -> "E"
           in 11..80 -> "NE"
           else -> "N"
       }
       text_view_degree.text = "$azimuth*$where"
}

private var accelerometer: Sensor? = null
private var magnetometer: Sensor? = null
private var haveSensorAccelerometer = false
private var haveSensorMagnetometer = false
private var rotationVector: Sensor? = null
private var haveSensorRotationVector = false

private fun startCompass(){
   if (sensorManager!!.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR) == null) {
       if (sensorManager!!.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) == null ||
           sensorManager!!.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)==null){
           noSensorAlert()
       } else {
           accelerometer = sensorManager!!.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
           magnetometer = sensorManager!!.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)

           haveSensorAccelerometer = sensorManager!!.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_UI)
           haveSensorMagnetometer = sensorManager!!.registerListener(this, magnetometer, SensorManager.SENSOR_DELAY_UI)
       }
   }else {
       rotationVector = sensorManager!!.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)
       haveSensorRotationVector = sensorManager!!.registerListener(this, rotationVector, SensorManager.SENSOR_DELAY_UI)
   }
}

private fun stopCompass(){
   if (haveSensorRotationVector) sensorManager!!.unregisterListener(this, rotationVector)
   if (haveSensorAccelerometer) sensorManager!!.unregisterListener(this, accelerometer)
   if (haveSensorMagnetometer) sensorManager!!.unregisterListener(this, magnetometer)
}

private fun noSensorAlert() {
   val alertDialog = AlertDialog.Builder(this)
   alertDialog.setMessage("Не поддерживает компас!").setCancelable(false).setNegativeButton("Закрыть") { _,_ -> finish() }
       
alertDialog.show()
}

override fun onResume() {
   super.onResume()
   startCompass()
}
override fun onPause() {
   super.onPause()
   stopCompass()
}
}

 


 

 

     


































































































































































































































































Приложение Б

Интерфейс программы «Track tracking»

 

 

               


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: