我想用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 ">
我怎么能像这样编辑,属性以及
我之所以尝试从JPGX改成自己,是因为我需要从航点获取时间。请在这里输入图像描述
有时候,我需要从WayPoint类中获取时间,但时间的类型是ZonedDateTime,但这在SDK 24上是行不通的......有什么办法可以从WayPoint中获取时间吗?
我在Gradle上添加了ThreeTenABP,但我不知道我到底如何使用它,我在Gradle上添加了库,并在app-instance中init,但它仍然会出错。
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)
gpx本质上是一个特殊的xml。你说的是 XML命名空间.
如何创建呢。创建XML头
一个xml文件只能有一个根元素,但有这么多的 trkpt
gpx文件中的元素。所以 trkpt
项目不应该是一个根元素。你应该包括所有的 trkpt
单元 gpx
元素,它是你的gpx文件的根元素。
要在Android中生成一个gpx文件,更好的选择是使用一个库。它们中的大多数都可以帮助你进行上述所有操作。
我现在使用的是 io.jenetics.jpx
在我的android项目中,它工作得很好。
XML不支持追加元素。如果你想在记录时生成一个gpx文件,你应该用一个可追加的形式来做,比如任何一个 Serializable
类型。完成记录后,您可以从序列化文件中创建一个有效的GPX文件。
该 WayPoint
in jpx是一个Serializable类型,我正在使用它。
你也可以用Android的 Location
,它实现了 Parcelable
,android实现像Serializable。
我的代码.jpx包含了zonedDateTime类,它在java8中工作。
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版本中。