【问题标题】:how to make gpx file in android如何在android中制作gpx文件
【发布时间】: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) 

【问题讨论】:

    标签: android xml kotlin dom gpx


    【解决方案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 - StackOverFlow

    【讨论】:

    • 其实我用的是 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 实例中初始化。但我认为这是行不通的。那么如何使用呢?我在帖子末尾添加了更多相关信息
    猜你喜欢
    • 2016-01-01
    • 2016-05-31
    • 2011-09-17
    • 2014-06-10
    • 2013-09-10
    • 2018-12-28
    • 1970-01-01
    • 2016-12-17
    • 1970-01-01
    相关资源
    最近更新 更多