如何在android中制作gpx文件
Posted
技术标签:
【中文标题】如何在android中制作gpx文件【英文标题】:how to make gpx file in android 【发布时间】:2020-08-15 19:54:49 【问题描述】:我想用 DOM 和 Transformer 编写 gpx 文件
我的代码是这样的
try
val document =
DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument()
val trkpt = document.createElement("trkpt")
trkpt.setAttribute("lat", "-33.626932")
trkpt.setAttribute("lon", "-33.626932")
val ele = document.createElement("ele")
ele.appendChild(document.createTextNode("-6"))
trkpt.appendChild(ele)
document.appendChild(trkpt)
val transformer = TransformerFactory.newInstance().newTransformer()
transformer.setOutputProperty(OutputKeys.METHOD,"gpx")
val saveFolder = File(folderPath) // 저장 경로
if (!saveFolder.exists()) //폴더 없으면 생성
saveFolder.mkdir()
val path = "route_$System.currentTimeMillis().gpx"
val file = File(saveFolder, path) //로컬에 파일저장
val source = DOMSource(document)
//val result = StreamResult(FileOutputStream(file))
val result = StreamResult(System.out)
transformer.transform(source, result)
return Uri.fromFile(file)
catch(e:Exception)
e.printStackTrace()
输出是这样的
<?xml version="1.0" encoding="UTF-8"?><trkpt lat="-33.626932" lon="-33.626932"><ele>-6</ele></trkpt>
但我想把标签改成这样
<gpx xmlns="http://www.topografix.com/GPX/1/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" creator="TraceDeTrail http://www.tracedetrail.fr" version="1.1" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd ">
我怎样才能这样编辑,属性也是如此
我尝试从 JPX 更改为我自己的原因是我需要从航点获取时间。 enter image description here
有时,我需要从 WayPoint 类中获取时间,但时间的类型是 ZonedDateTime。但它不适用于 SDK 24...是否有任何解决方案从航点获取时间?
我添加了 ThreeTenABP,但我不知道我是如何使用它的。 我在 Gradle 上添加库并在 app-instance 中初始化 但它仍然会出错
wpList.add(
WayPoint.builder()
.lat(currentLatLng.latitude)
.lon(currentLatLng.longitude)
.name("Start")
.desc("Start Description")
.time(System.currentTimeMillis()) <- RunningActivity.kt:107
.type(START_POINT)
.build()
)
2020-05-06 16:46:16.895 8877-8877/com.umpa2020.tracer E/androidRuntime: FATAL EXCEPTION: main
Process: com.umpa2020.tracer, PID: 8877
java.lang.NoClassDefFoundError: Failed resolution of: Ljava/time/Instant;
at io.jenetics.jpx.WayPoint$Builder.time(WayPoint.java:767)
at com.umpa2020.tracer.main.start.running.RunningActivity.start(RunningActivity.kt:107)
at com.umpa2020.tracer.main.start.running.RunningActivity.onSingleClick(RunningActivity.kt:175)
at com.umpa2020.tracer.util.OnSingleClickListener$DefaultImpls.onClick(OnSingleClickListener.kt:19)
at com.umpa2020.tracer.main.start.BaseRunningActivity.onClick(BaseRunningActivity.kt:45)
at android.view.View.performClick(View.java:5610)
at android.view.View$PerformClick.run(View.java:22265)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6077)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
Caused by: java.lang.ClassNotFoundException: Didn't find class "java.time.Instant" on path: DexPathList[[zip file "/data/app/com.umpa2020.tracer-2/base.apk"],nativeLibraryDirectories=[/data/app/com.umpa2020.tracer-2/lib/x86, /system/lib, /vendor/lib]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
at java.lang.ClassLoader.loadClass(ClassLoader.java:380)
at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
at io.jenetics.jpx.WayPoint$Builder.time(WayPoint.java:767)
at com.umpa2020.tracer.main.start.running.RunningActivity.start(RunningActivity.kt:107)
at com.umpa2020.tracer.main.start.running.RunningActivity.onSingleClick(RunningActivity.kt:175)
at com.umpa2020.tracer.util.OnSingleClickListener$DefaultImpls.onClick(OnSingleClickListener.kt:19)
at com.umpa2020.tracer.main.start.BaseRunningActivity.onClick(BaseRunningActivity.kt:45)
at android.view.View.performClick(View.java:5610)
at android.view.View$PerformClick.run(View.java:22265)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6077)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
【问题讨论】:
【参考方案1】:gpx 本质上是一个特殊的 xml。您所说的是 XML 命名空间。
创建方法:ho to create XML header
一个xml文件只能有一个根元素,但一个gpx文件中有这么多trkpt
元素。所以trkpt
item 不应该是根元素。您应该将所有 trkpt
元素包含在单个 gpx
元素中,这是 gpx 文件的根元素。
要在 Android 中生成 gpx 文件,更好的选择是使用库。他们中的大多数可以帮助您进行上述所有操作。
我在我的 android 项目中使用io.jenetics.jpx
,它工作正常。
XML 不支持附加元素。如果您想在记录时生成 gpx 文件,您应该以可附加的形式执行此操作,例如任何 Serializable
类型。完成记录后,您可以从序列化文件创建一个有效的 GPX 文件。
jpx中的WayPoint
是Serializable类型,我正在使用。
你也可以使用AndroidLocation
,它实现了Parcelable
,类似Serializable的android实现。
我的代码:
public class TrackService extends Service
private int notificationId = 142857 ;
NotificationCompat.Builder builder;
NotificationManagerCompat notificationManager;
private Timer timer = new Timer(true);
private static final String CHANNEL_ID = "org.kib.qtp";
private File serializerFile;
private ObjectOutputStream oos;
private Long basetime;
SimpleDateFormat sdf;
@Override
public void onCreate()
super.onCreate();
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), 0);
builder = new NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_map_black_24dp)
.setContentTitle(getString(R.string.track))
.setContentText(getString(R.string.tracktext))
.setPriority(NotificationCompat.PRIORITY_LOW)
.setOngoing(true)
.setOnlyAlertOnce(true)
.setContentIntent(pendingIntent);
notificationManager = NotificationManagerCompat.from(this);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
startForeground (notificationId, builder.build());
else
notificationManager.notify(notificationId, builder.build());
@Override
public void onDestroy()
try
endTrack();
catch (IOException e)
e.printStackTrace();
stopForeground(true);
super.onDestroy();
@Nullable
@Override
public IBinder onBind(Intent intent)
return null;
@Override
public int onStartCommand(Intent intent, int flags, int startId)
/**
* @param ele require altitude or not
* @param time update time
* @param fine fine location or not
* @param name the gpx file's name
*/
sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", getResources(). getConfiguration().locale);
int time = 60000;
boolean ele = false;
boolean fine = false;
String name = Calendar.getInstance().getTime().toString().replaceAll(" ", "_");
if (intent.hasExtra("time"))
Objects.requireNonNull(intent.getExtras()).getInt("time");
if (intent.hasExtra("ele"))
Objects.requireNonNull(intent.getExtras()).getBoolean("ele");
if (intent.hasExtra("fine"))
Objects.requireNonNull(intent.getExtras()).getBoolean("fine");
if (intent.hasExtra("name"))
Objects.requireNonNull(intent.getExtras()).getString("name");
File path = new File(getFilesDir().getAbsolutePath() + "/gpx");
while(!path.exists())
path.mkdirs();
try
serializerFile = new File(getFilesDir().getAbsolutePath() + "/gpx/" + name );
if (serializerFile.exists())
serializerFile.delete();
while(!serializerFile.exists())
serializerFile.createNewFile();
oos = new ObjectOutputStream(new FileOutputStream(serializerFile));
catch (IOException e)
e.printStackTrace();
basetime = 0L;
Toast.makeText(this, R.string.toast_start_tracking, Toast.LENGTH_SHORT).show();
startTrack(time, ele, fine);
return START_STICKY;
private void endTrack() throws IOException
timer.cancel();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(serializerFile));
TrackSegment.Builder segment = TrackSegment.builder();
while (true)
try
WayPoint wp = (WayPoint) ois.readObject();
segment.addPoint(wp);
catch (EOFException e)
break;
catch (ClassNotFoundException ignored)
final GPX gpx = GPX.builder().addTrack(track -> track.addSegment(segment.build())).build();
GPX.write(gpx, serializerFile.getAbsolutePath() + ".gpx");
Toast.makeText(this, R.string.toast_gpx_write, Toast.LENGTH_SHORT).show();
new File(serializerFile.getAbsolutePath() + ".tobeupload").createNewFile();;
private void startTrack(int time, boolean ele, boolean fine)
TimerTask task = new TimerTask()
public void run()
Location location = getLocation(ele, fine);
if (location != null)
try
if (location.getTime() != basetime)
basetime = location.getTime();
WayPoint point = WayPoint.builder().lat(location.getLatitude()).lon(location.getLongitude()).ele(location.getAltitude()).time(location.getTime()).build();
if (point != null)
oos.writeObject(point);
NotificationCompat.Builder timerBuilder = builder.setContentText(getString(R.string.tracktext) + sdf.format(location.getTime()));
Log.i("track", location.getLatitude() +","+ location.getLongitude() +","+ location.getAltitude());
notificationManager.notify(notificationId,timerBuilder.build());
catch (IOException e)
e.printStackTrace();
;
timer.schedule(task, 0, time);
private Location getLocation(boolean ele, boolean fine)
//get location here
jpx 包括 zonedDateTime 类,它在 java8 中工作。但它的创建者 Stephen Colebourne 将它向后移植到 Java 6,并且作为库也向后移植到了早期的 android 版本。
图书馆在这里:JakeWharton/ThreeTenABP - An adaptation of the JSR-310 backport for Android.
以及如何使用:How to use ThreeTenABP in Android Project - ***
【讨论】:
其实我用的是 JPX 库。但我的应用程序的最低 sdk 版本是 24。这意味着我需要使用 java 7。但是 jpx 库包含 zoneddatetime 类......它适用于 java8。所以我想我需要创建自己......你知道其他图书馆吗?? jpx 在我的项目中运行良好,它的 min sdk 为 21。您可以使用 Long 变量作为时间。例如 location.getTime()。 @Yunkwon umm.. 我从 jpx 更改为我自己的原因是,我需要从航路点获取时间...我编辑帖子的结尾。请查看并回复..我需要你的帮助... 好的,我明白了。 java 8 中的 java.time.* 包已由其创建者 Stephen Colebourne 向后移植到 Java 6,并且也向后移植到早期的 android 版本。在这里:github.com/JakeWharton/ThreeTenABP 我又有一个问题...我将 ThreeTenABP 添加到我的项目中。并在我的 App 实例中初始化。但我认为这是行不通的。那么如何使用呢?我在帖子末尾添加了更多相关信息以上是关于如何在android中制作gpx文件的主要内容,如果未能解决你的问题,请参考以下文章