Subversion Repositories mkgmap

Rev

Rev 4689 | View as "text/plain" | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * Copyright (C) 2012.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 or
 * version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 */


package uk.me.parabola.mkgmap.sea.optional;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Pattern;
import java.util.zip.GZIPOutputStream;

import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import uk.me.parabola.imgfmt.ExitException;
import uk.me.parabola.imgfmt.Utils;
import uk.me.parabola.imgfmt.app.Coord;
import uk.me.parabola.mkgmap.reader.osm.SeaGenerator;
import uk.me.parabola.mkgmap.reader.osm.Way;
import uk.me.parabola.splitter.BinaryMapWriter;
import uk.me.parabola.splitter.Node;
import uk.me.parabola.splitter.OSMWriter;
import uk.me.parabola.splitter.OSMXMLWriter;

class PrecompSeaSaver implements Runnable {
        private final AtomicBoolean finished = new AtomicBoolean(false);
        private final CountDownLatch finishWait;

        private final Map<String, String> index;

        private final Map<Integer, String> idMapping;
        private int nextId = 0;
        private final boolean usePbf;

        private final File outputDir;
       
        private final BlockingQueue<Entry<String, List<Way>>> saveQueue = new LinkedBlockingQueue<>();

        public PrecompSeaSaver(File outputDir, boolean usePbf) {
                this.outputDir = outputDir;
                finishWait = new CountDownLatch(1);
                this.usePbf = usePbf;
                idMapping = new HashMap<>();
                index = new TreeMap<>();
                this.outputDir.mkdirs();
        }
       
        public BlockingQueue<Entry<String, List<Way>>> getQueue() {
                return saveQueue;
        }

        private OSMWriter createWriter(int id, String key) {
                String[] parts = key.split(Pattern.quote("_"));
                int lat = Integer.parseInt(parts[0]);
                int lon = Integer.parseInt(parts[1]);
                uk.me.parabola.splitter.Area bounds = new uk.me.parabola.splitter.Area(lat, lon,
                                lat + SeaGenerator.PRECOMP_RASTER, lon + SeaGenerator.PRECOMP_RASTER);
                OSMWriter writer = (usePbf ? new BinaryMapWriter(bounds, outputDir, nextId, 0)
                                : new OSMXMLWriter(bounds, outputDir, nextId, 0));
                idMapping.put(id, key);
                writer.initForWrite();
                return writer;
        }
       
        public void waitForFinish() throws InterruptedException {
                this.finished.set(true);
                this.finishWait.await();
        }

        public void run() {
                while (!saveQueue.isEmpty() || !finished.get()) {
                        Entry<String, List<Way>> tileData = null;
                        try {
                                tileData = saveQueue.poll(5, TimeUnit.SECONDS);
                        } catch (InterruptedException exp) {
                                exp.printStackTrace();
                        }
                        if (tileData != null) {
                                int fakeMapid = ++nextId;

                                if (tileData.getValue().size() == 1) {
                                        // do not write the tile because it consists of one
                                        // natural type only
                                        // write it only to the index
                                        Way singleWay = tileData.getValue().get(0);
                                        String naturalTag = singleWay.getTag("natural");
                                        index.put(tileData.getKey(), naturalTag);
                                } else {
                                        try {
                                                writeTile(tileData, fakeMapid);
                                        } catch (IOException e) {
                                                throw new ExitException(e.getLocalizedMessage());
                                        }
                                }
                        }
                }
               
                try {
                        System.out.println("Writing index file");
                        writeIndex();
                } catch (IOException e) {
                        e.printStackTrace();
                }

                finishWait.countDown();
        }
       
        /**
         * Use splitter.jar to write the tile in osm format.
         * @param tileData the tile data containing the
         * @param fakeMapid the pseudo-mapid used for this tile
         * @throws IOException in case of error
         */

        private void writeTile(Entry<String, List<Way>> tileData, int fakeMapid) throws IOException {
                String ext = (usePbf ? "pbf" : "gz");
                index.put(tileData.getKey(), "sea_" + tileData.getKey() + ".osm." + ext);

                OSMWriter writer = createWriter(fakeMapid, tileData.getKey());

                Long2ObjectOpenHashMap<Long> coordIds = new Long2ObjectOpenHashMap<>();
                Map<Long,uk.me.parabola.splitter.Way> pbfWays = new TreeMap<>();
                long maxNodeId = 1;
                for (Way w : tileData.getValue()) {
                        uk.me.parabola.splitter.Way pbfWay = new uk.me.parabola.splitter.Way();
                        pbfWay.set(w.getId());
                        for (Entry<String, String> tag : w.getTagEntryIterator()) {
                                pbfWay.addTag(tag.getKey(), tag.getValue());
                        }
                        for (Coord c : w.getPoints()) {
                                long key = Utils.coord2Long(c);
                                Long nodeId = coordIds.get(key);
                                if (nodeId == null) {
                                        nodeId = maxNodeId++;
                                        coordIds.put(key, nodeId);
                                        Node n = new Node();
                                        n.set(nodeId, c.getLatDegrees(), c.getLonDegrees());
                                        writer.write(n);
                                }
                                pbfWay.addRef(nodeId);
                        }
                        pbfWays.put(pbfWay.getId(), pbfWay);
                }
                for (uk.me.parabola.splitter.Way pbfWay : pbfWays.values()) {
                        writer.write(pbfWay);
                }

                writer.finishWrite();

                File tileFile = new File(outputDir, String.format("%08d.osm.%s", fakeMapid, ext));
                File precompFile = new File(outputDir, "sea_" + tileData.getKey() + ".osm." + ext);
                Files.move(tileFile.toPath(), precompFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
        }

        private void writeIndex() throws IOException {
                try (PrintWriter indexWriter = new PrintWriter(
                                new GZIPOutputStream(new FileOutputStream(new File(outputDir, "index.txt.gz"))))) {

                        for (Entry<String, String> ind : index.entrySet()) {
                                indexWriter.format("%s;%s", ind.getKey(), ind.getValue()).append('\n');
                        }
                }
        }
}