/* * Copyright (C) 2008 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License 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. * * Create date: 07-Jul-2008 */ package uk.me.parabola.imgfmt.app.net; import java.util.List; import uk.me.parabola.imgfmt.app.Area; import uk.me.parabola.imgfmt.app.Coord; import uk.me.parabola.imgfmt.app.ImgFileWriter; import uk.me.parabola.log.Logger; /** * Routing nodes are divided into areas which I am calling RouteCenter's. * The center has a location and it contains nodes that are nearby. * There is routing between nodes in the center and there are links * to nodes in other centers. */ public class RouteCenter { private static final Logger log = Logger.getLogger(RouteCenter.class); private final Area area; private final Coord centralPoint; private final List<RouteNode> nodes; private final TableA tabA; private final TableB tabB; private final TableC tabC; public RouteCenter(Area area, List<RouteNode> nodes, TableA tabA, TableB tabB) { this.area = area; this.centralPoint = area.getCenter(); this.nodes = nodes; this.tabA = tabA; this.tabB = tabB; this.tabC = new TableC(tabA); log.info("new RouteCenter at " + centralPoint.toDegreeString() + ", nodes: " + nodes.size() + " tabA: " + tabA.size() + " tabB: " + tabB.size()); } /** * update arcs with table indices; populate tabC */ private void updateOffsets(){ for (RouteNode node : nodes) { node.setOffsets(centralPoint); for (RouteArc arc : node.arcsIteration()) { arc.setIndexA(tabA.getIndex(arc)); arc.setInternal(nodes.contains(arc.getDest())); if (!arc.isInternal()) arc.setIndexB(tabB.getIndex(arc.getDest())); } for (RouteRestriction restr : node.getRestrictions()){ if (restr.getArcs().size() >= 3){ // only restrictions with more than 2 arcs can contain further arcs for (RouteArc arc : restr.getArcs()){ if (arc.getSource() == node) continue; arc.setIndexA(tabA.getIndex(arc)); arc.setInternal(nodes.contains(arc.getDest())); if (!arc.isInternal()) arc.setIndexB(tabB.getIndex(arc.getDest())); } } restr.setOffsetC(tabC.addRestriction(restr)); } } // update size of tabC offsets, now that tabC has been populated tabC.propagateSizeBytes(); } /** * Write a route center. * * writer.position() is relative to the start of NOD 1. * Space for Table A is reserved but not written. See writeTableA. */ public void write(ImgFileWriter writer, int[] classBoundaries) { assert !nodes.isEmpty(): "RouteCenter without nodes"; updateOffsets(); int centerPos = writer.position(); for (RouteNode node : nodes){ node.write(writer); int group = node.getGroup(); if (group == 0) continue; if (centerPos < classBoundaries[group-1]){ // update positions (loop is used because style might not use all classes for (int i = group-1; i >= 0; i--){ if (centerPos < classBoundaries[i] ) classBoundaries[i] = centerPos; } } } int alignment = 1 << NODHeader.DEF_ALIGN; int alignMask = alignment - 1; // Calculate the position of the tables. int tablesOffset = (writer.position() + alignment) & ~alignMask; log.debug("write table a at offset", Integer.toHexString(tablesOffset)); // Go back and fill in all the table offsets for (RouteNode node : nodes) { int pos = node.getOffsetNod1(); log.debug("node pos", pos); byte bo = (byte) calcLowByte(pos, tablesOffset); writer.position(pos); log.debug("rewrite taba offset", writer.position(), bo); writer.put(bo); // fill in arc pointers node.writeSecond(writer); } writer.position(tablesOffset); // Write the tables header writer.put(tabC.getFormat()); writer.put3(centralPoint.getLongitude()); writer.put3(centralPoint.getLatitude()); writer.put(tabA.getNumberOfItems()); writer.put(tabB.getNumberOfItems()); tabA.write(writer); tabB.write(writer); tabC.write(writer, tablesOffset); log.info("end of center:", writer.position()); } public void writePost(ImgFileWriter writer) { // NET addresses are now known tabA.writePost(writer); // all RouteNodes now have their NOD1 offsets tabB.writePost(writer); } /** * Inverse of calcTableOffset. */ private static int calcLowByte(int nodeOffset, int tablesOffset) { assert nodeOffset < tablesOffset; int align = NODHeader.DEF_ALIGN; int mask = (1 << align) - 1; if ((tablesOffset & mask) != 0) { log.warn("tablesOffset not a multiple of (1<<align): %x", tablesOffset); // round up to next multiple tablesOffset = ((tablesOffset >> align) + 1) << align; } int low = (tablesOffset >> align) - (nodeOffset >> align) - 1; assert 0 <= low && low < 0x100; return low; } public Area getArea() { return area; } public String reportSizes() { int nodesSize = 0; for(RouteNode n : nodes) nodesSize += n.boundSize(); return "n=(" + nodes.size() + "," + nodesSize + "), a=" + tabA.size() + ", b=" + tabB.size(); } }