Package com.bbn.openmap.layer.mif

Source Code of com.bbn.openmap.layer.mif.MIFLoader

// **********************************************************************
//
// <copyright>
//
//  BBN Technologies
//  10 Moulton Street
//  Cambridge, MA 02138
//  (617) 873-8000
//
//  Copyright (C) BBNT Solutions LLC. All rights reserved.
//
// </copyright>
// **********************************************************************
//
// $Source: /cvs/distapps/openmap/src/openmap/com/bbn/openmap/layer/mif/MIFLoader.java,v $
// $RCSfile: MIFLoader.java,v $
// $Revision: 1.4.2.2 $
// $Date: 2005/08/09 21:17:57 $
// $Author: dietrick $
//
// **********************************************************************

package com.bbn.openmap.layer.mif;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.StringTokenizer;
import java.util.Vector;

import com.bbn.openmap.omGraphics.OMGraphic;
import com.bbn.openmap.omGraphics.OMGraphicConstants;
import com.bbn.openmap.omGraphics.OMGraphicList;
import com.bbn.openmap.omGraphics.OMLine;
import com.bbn.openmap.omGraphics.OMPoint;
import com.bbn.openmap.omGraphics.OMPoly;
import com.bbn.openmap.omGraphics.OMText;
import com.bbn.openmap.util.Debug;

/**
* A loader class for MIF files. Each MIF layer loading a file will
* create an instance of this The class uses SwingWorker to do
* processing in a thread Only the MIF PLine and Region options are
* implemented
*
* 27th January 2004 - added some support for TEXT and POINT options
*
* @author Colin Mummery, modified January 2004 by Simon Bowen
*/
public class MIFLoader {
    final static int PROCESS_HEADER = 0;
    final static int PROCESS_DATA = 1;
    final static int PROCESS_PLINE = 2;
    final static int PROCESS_POST_PLINE = 3;
    final static int PROCESS_MULTIPLE = 4;
    final static int PROCESS_REGION = 5;
    final static int PROCESS_REGION_HEADER = 6;
    final static int PROCESS_POST_REGION = 7;
    final static int PROCESS_POST_LINE = 8;

    final static String DATA_WORD = "Data";
    final static String VERSION_WORD = "Version";
    final static String DELIMITER_WORD = "Delimiter";
    final static String COORDSYS_WORD = "Coordsys";
    final static String PLINE_WORD = "PLine";
    final static String LINE_WORD = "Line";
    final static String MULTIPLE_WORD = "Multiple";
    final static String PEN_WORD = "Pen";
    final static String SMOOTH_WORD = "Smooth";
    final static String REGION_WORD = "Region";
    final static String BRUSH_WORD = "Brush";
    final static String CENTER_WORD = "Center";
    final static String POINT_WORD = "Point";
    final static String TEXT_WORD = "Text";

    BufferedReader br;
    OMGraphicList list;

    // if true we do a much faster line only rendering of the regions
    boolean accurate;

    // MIF CoordSys value for a Latitude Longitude coodinate system
    private static final String LATLONG_COORDSYS_DEF = "Earth Projection 1";

    private float pointVisible = -1; // default is -1
    private float textVisible = -1; // default is -1

    /**
     * Loads a MIF file from the Reader and placing the appropriate
     * OMGraphics on the OMGraphicList * Parsing is done by a simple
     * loop and switch statements
     *
     * @param br BufferedReader to read the MIF file
     * @param accurate if true we do a much faster line only rendering
     *        of the regions
     * @param textVisible the scale at which TEXT primitives should be
     *        rendered
     * @param pointVisible the scale at which POINT primitives should
     *        be rendered
     */
    public MIFLoader(BufferedReader br, boolean accurate, float textVisible,
            float pointVisible) {
        super();
        this.br = br;
        this.accurate = accurate;
        this.pointVisible = pointVisible;
        this.textVisible = textVisible;
    }

    public boolean isLoaded() {
        return list != null;
    }

    /**
     * Get the OMGraphicList from the loader, creating it from the
     * file if it hasn't been created yet.
     */
    public OMGraphicList getList() {
        return getList(false);
    }

    /**
     * Get the OMGraphicList from the loader, with the option of
     * forcing it to be recreated from the source file if desired.
     */
    public OMGraphicList getList(boolean reloadList) {
        try {
            if (reloadList || !isLoaded()) {
                if (isLoaded())
                    list.clear();
                list = loadFile();
            }
            return list;
        } catch (IOException ioe) {
            list = null;
            // } catch(MIFException mex) {
            // list = null;
            // Debug.error(mex.getMessage());
            // mex.printStackTrace();
        }
        return null;
    }

    public OMGraphicList loadFile() throws IOException, MIFException {
        float[] ptarray = null;

        // Used by region to do the polygon calculation
        float[] latpts = null;
        float[] lonpts = null;

        // Specifies the expected next action in the loop
        int action = PROCESS_HEADER;
        int number = 0;
        int count = 0;
        int multiple = 0;
        int multicnt = 0;

        // setting to true means we don't read the same line again
        boolean pushback;
        StringTokenizer st = null;
        String tok = null;
        pushback = false;
        int idx;
        OMPoly omp = null;
        OMLine oml = null;
        MIFPoint ompoint = null;
        OMText omtext = null;
        boolean ismultiple = false;

        OMGraphicList aList = new OMGraphicList();

        // a vector of omgraphics for regions that allows adding and
        // deleting
        Vector omgs = new Vector();

        MAIN_LOOP: while (true) {

            if (!pushback) {
                // if it's null then there's no more
                if ((st = getTokens(br)) == null)
                    break MAIN_LOOP;

                tok = st.nextToken();
            } else {
                pushback = false; // pushback was true so make it
                // false so it doesn't happen twice
            }

            SWITCH: switch (action) {

            case PROCESS_HEADER:
                if (isSame(tok, DATA_WORD)) {
                    action = PROCESS_DATA;
                } else if (isSame(tok, VERSION_WORD)) {
                } else if (isSame(tok, DELIMITER_WORD)) {
                } else if (isSame(tok, COORDSYS_WORD)) {
                    // check the CoordSys header, OpenMap only
                    // directly
                    // supports LatLong type of coordsys
                    String coordSysLine = COORDSYS_WORD;
                    while (st.hasMoreElements()) {
                        coordSysLine += " " + st.nextElement();
                    }

                    String goodCoordSys = COORDSYS_WORD + " "
                            + LATLONG_COORDSYS_DEF;
                    if (goodCoordSys.length() < coordSysLine.length()) {
                        coordSysLine = coordSysLine.substring(0,
                                goodCoordSys.length());
                    } else {
                        goodCoordSys = goodCoordSys.substring(0,
                                coordSysLine.length());
                    }

                    // check that the CoordSys header matches the MIF
                    // specification for LatLong type
                    if (!isSame(coordSysLine, goodCoordSys)) {
                        Debug.error("MIFLoader file has coordinate system: "
                                + coordSysLine + ", requires " + goodCoordSys);
                        // raise error, as the coordsys header was
                        // invalid
                        throw new MIFException("File appears to contain objects with an incompatible coordinate system (Must be Lat/Lon).");
                    }

                }
                break SWITCH;

            case PROCESS_DATA:
                omgs.clear();
                if (isSame(tok, PLINE_WORD)) {
                    tok = st.nextToken();
                    if (isSame(tok, MULTIPLE_WORD)) {
                        multiple = Integer.parseInt(st.nextToken());
                        multicnt = 0;
                        action = PROCESS_MULTIPLE;
                        ismultiple = true;
                    } else {
                        number = Integer.parseInt(tok);
                        ptarray = new float[number + number];
                        count = 0;
                        action = PROCESS_PLINE;
                    }
                } else if (isSame(tok, REGION_WORD)) {
                    multiple = Integer.parseInt(st.nextToken());
                    multicnt = 0;
                    action = PROCESS_REGION_HEADER;
                } else if (isSame(tok, LINE_WORD)) {
                    float lon1 = Float.parseFloat(st.nextToken());
                    float lat1 = Float.parseFloat(st.nextToken());
                    float lon2 = Float.parseFloat(st.nextToken());
                    float lat2 = Float.parseFloat(st.nextToken());

                    oml = new OMLine(lat1, lon1, lat2, lon2, OMGraphicConstants.LINETYPE_STRAIGHT);

                    action = PROCESS_POST_LINE;
                } else if (isSame(tok, POINT_WORD)) // handle a MIF
                // POINT primitive
                {
                    // get the coordinates
                    float lon1 = Float.parseFloat(st.nextToken());
                    float lat1 = Float.parseFloat(st.nextToken());

                    // construct the OM graphic
                    ompoint = new MIFPoint(lat1, lon1, pointVisible);
                    st = getTokens(br);

                    // set the graphics attributes
                    this.processSymbolWord(st, ompoint);

                    // add to the graphic list for this layer
                    aList.add(ompoint);
                    action = PROCESS_DATA;
                } else if (isSame(tok, TEXT_WORD)) // handle a MIF
                // TEXT primitive
                {
                    String textString = "";

                    // if the actual text is not on the same line as
                    // the primitive declaration
                    if (st.countTokens() < 1) {
                        // get the next line
                        st = getTokens(br);
                    }
                    // build up the display text string,
                    while (st.hasMoreTokens()) {
                        textString += st.nextToken();
                    }

                    if (textString.length() >= 1) {
                        // remove any surrounding " characters
                        textString = textString.substring(1,
                                textString.length() - 1);
                    }
                    // get the next line, it contains the coordinates
                    st = getTokens(br);

                    float lon1 = Float.parseFloat(st.nextToken());
                    float lat1 = Float.parseFloat(st.nextToken());
                    /* float lon2 = */Float.parseFloat(st.nextToken());
                    /* float lat2 = */Float.parseFloat(st.nextToken());
                    // create the OMGraphic for the text object
                    omtext = new MIFText(lat1, lon1, textString, OMText.JUSTIFY_CENTER, textVisible);

                    // the next line contains the text attributes
                    st = getTokens(br);
                    // set the attributes agains the omgraphic
                    this.processFontWord(st, omtext);
                    // add to the layers graphic list
                    aList.add(omtext);

                    action = PROCESS_DATA;
                }
                break SWITCH;

            // We have a line, tok is the first coord and the next
            // token is the second
            case PROCESS_PLINE:
                idx = count + count;
                ptarray[idx + 1] = Float.parseFloat(tok);
                ptarray[idx] = Float.parseFloat(st.nextToken());
                count++;
                if (count == number) {
                    omp = new OMPoly(ptarray, OMGraphic.DECIMAL_DEGREES, OMGraphic.LINETYPE_STRAIGHT);

                    aList.add(omp);
                    if (!ismultiple) {
                        action = PROCESS_POST_PLINE;
                    } else {
                        omgs.add(omp);
                        action = PROCESS_MULTIPLE;
                    }
                }
                break SWITCH;

            case PROCESS_MULTIPLE:
                multicnt++;
                if (multicnt > multiple) { // No more multiples so we
                    // can pushback
                    pushback = true;
                    multiple = 0;
                    action = PROCESS_POST_PLINE;
                    break SWITCH;
                }
                number = Integer.parseInt(tok);
                count = 0;
                ptarray = new float[number + number];
                action = PROCESS_PLINE;
                break SWITCH;

            case PROCESS_POST_PLINE:
                if (isSame(tok, PEN_WORD)) {
                    if (ismultiple) {
                        processPenWord(st, omgs);
                    } else {
                        processPenWord(st, omp);
                    }
                } else if (isSame(tok, SMOOTH_WORD)) {
                    // Smooth unimplemented
                } else {
                    ismultiple = false;
                    pushback = true;
                    action = PROCESS_DATA;
                }
                break SWITCH;

            // SCN to support lines
            case PROCESS_POST_LINE:
                if (isSame(tok, PEN_WORD)) {
                    processPenWord(st, oml);
                    aList.add(oml);
                } else {
                    ismultiple = false;
                    pushback = true;
                    action = PROCESS_DATA;
                }
                break SWITCH;

            case PROCESS_REGION_HEADER: // This processes the number
                // at the top of each region
                // sub-block
                multicnt++;
                if (multicnt > multiple) {
                    multiple = 0;
                    action = PROCESS_POST_REGION;

                    // Add this point the region is finished so add
                    // the
                    // vector contents to list
                    int len = omgs.size();
                    for (int i = 0; i < len; i++) {
                        aList.add((OMGraphic) omgs.elementAt(i));
                    }
                    break SWITCH;
                }
                number = Integer.parseInt(tok);
                count = 0;
                ptarray = new float[number + number];
                latpts = new float[number];
                lonpts = new float[number];
                action = PROCESS_REGION;
                break SWITCH;

            case PROCESS_REGION:
                idx = count + count;
                lonpts[count] = ptarray[idx + 1] = Float.parseFloat(tok);
                latpts[count] = ptarray[idx] = Float.parseFloat(st.nextToken());
                count++;
                if (count == number) {
                    // This polygon is complete so add it and process
                    // the next

                    // Use this code if we just want polygons which is
                    // much
                    // faster
                    if (accurate) {
                        omgs.add(new OMSubtraction(latpts, lonpts));

                    } else {
                        // Produces accurate MapInfo type rendering
                        // but very
                        // slow with complex regions like streets
                        int end = latpts.length - 1;

                        for (int i = 0; i < end; i++) {
                            omgs.add(new OMLine(latpts[i], lonpts[i], latpts[i + 1], lonpts[i + 1], OMGraphic.LINETYPE_STRAIGHT));
                        }
                        omgs.add(new OMLine(latpts[end], lonpts[end], latpts[0], lonpts[0], OMGraphic.LINETYPE_STRAIGHT));
                    }
                    action = PROCESS_REGION_HEADER;
                }
                break SWITCH;

            // There is one pen,brush,center block at the end of a
            // region
            case PROCESS_POST_REGION:
                if (isSame(tok, PEN_WORD)) {
                    processPenWord(st, omgs);
                } else if (isSame(tok, BRUSH_WORD)) {
                    processBrushWord(st, omgs);
                } else if (isSame(tok, CENTER_WORD)) {
                } else {
                    pushback = true;
                    action = PROCESS_DATA;
                }
                break SWITCH;

            } // end of switch
        } // end of while loop

        br.close();

        return aList;
    }

    /*
     * Processes an instance of the Pen directive for a single
     * OMGraphic
     */
    private void processPenWord(StringTokenizer st, OMGraphic omg) {
        if (omg == null)
            return;
        int width = Integer.parseInt(st.nextToken());
        omg.setStroke(new BasicStroke(width));
        /* int pattern = */Integer.parseInt(st.nextToken());
        Color col = convertColor(Integer.parseInt(st.nextToken()));
        omg.setLinePaint(col);
    }

    /*
     * Processes an instance of the Pen directive for a vector of
     * OMGraphics
     */
    private void processPenWord(StringTokenizer st, Vector vals) {
        int width = Integer.parseInt(st.nextToken());
        /* int pattern = */Integer.parseInt(st.nextToken());
        Color col = convertColor(Integer.parseInt(st.nextToken()));
        int len = vals.size();
        OMGraphic omg = null;
        for (int i = 0; i < len; i++) {
            omg = (OMGraphic) vals.elementAt(i);
            omg.setLinePaint(col);
            omg.setStroke(new BasicStroke(width));
        }
    }

    /*
     * Processes an instance of the Brush directive
     */
    private void processBrushWord(StringTokenizer st, Vector vals) {

        int pattern = Integer.parseInt(st.nextToken());
        Color foreground = convertColor(Integer.parseInt(st.nextToken()));

        /*
         * background appears to be ignored by MapInfo but I grab it
         * anyway
         */
//        Color background = null;
//        if (st.hasMoreTokens()) {
//            background = convertColor(Integer.parseInt(st.nextToken()));
//        }
 
        int len = vals.size();
        OMGraphic omg;
        for (int i = 0; i < len; i++) {
            omg = (OMGraphic) vals.elementAt(i);
            omg.setLinePaint(foreground);

            switch (pattern) {
            case 1:
                break; // No fill so do nothing
            case 2:
                omg.setFillPaint(foreground);
                break;
            }
        }
    }

    /**
     * process the MIF SYMBOL element.
     *
     * The MIF format for SYMBOL element is <code>
     *  SYMBOL (shape, color, size, fontname, fontstyle, rotation)
     * </code>
     * or <code>
     *  SYMBOL (filename, color, size, customstyle)
     * </code>
     *
     * currently only the color attribute is considered and a default
     * OMPoint symbol and size is adopted.
     *
     * @param st tokenizer containing the "SYMBOL" MIF elements
     * @param omg the OMGraphic object to attribute with the setting
     *        from the MIF line
     */
    private void processSymbolWord(StringTokenizer st, OMPoint omg) {
        /*String symbolStr = */st.nextToken(); // should be "SYMBOL"

        /*int symbol = */Integer.parseInt(st.nextToken());
        Color color = convertColor(Integer.parseInt(st.nextToken()));

        /*int size = */Integer.parseInt(st.nextToken());

        omg.setFillPaint(color);
    }

    /**
     * process the MIF FONT element. currently only PLAIN (0),
     * BOLD(1), ITALIC (2) and BOLD ITALIC(3) are supported. Font size
     * is hardcoded to 10, and backcolor is ignored.
     *
     * The MIF format for FONT is <code>
     *  FONT ("fontname", style, size, forecolor [, backcolor] )
     * </code>
     *
     * Within a MIF file size will always be 0, it's up to the
     * renderer to determine the size. Background color is optional
     *
     * style is detemined as follows, to specify 2 or more style
     * attributes, add the values from each style, e.g. BOLD ALLCAPS =
     * 513
     *
     * value style =================== 0 PLAIN 1 BOLD 2 ITALIC 4
     * UNDERLINE 16 OUTLINE 32 SHADOW 256 HALO 512 ALL CAPS 1024
     * Expanded
     *
     *
     * @param st tokenizer containing the "FONT" MIF elements
     * @param omTxt the OMGraphic object to attribute with the setting
     *        from the MIF line
     */
    private void processFontWord(StringTokenizer st, OMText omTxt) {
        /*String fontStr = */st.nextToken(); // should be "FONT"
        String fontName = st.nextToken();
        int style = Integer.parseInt(st.nextToken());
        /*int size = */Integer.parseInt(st.nextToken());
        Color foreColor = convertColor(Integer.parseInt(st.nextToken()));

        // last token is optional background color
//        Color bgColor = null;
//        if (st.hasMoreTokens()) {
//            bgColor = convertColor(Integer.parseInt(st.nextToken()));
//        }

        int fontStyle = Font.PLAIN;
        switch (style) {
        case 0:
            fontStyle = Font.PLAIN;
            break;
        case 1:
            fontStyle = Font.BOLD;
            break;
        case 2:
            fontStyle = Font.ITALIC;
            break;
        case 3:
            fontStyle = Font.BOLD & Font.ITALIC;
            break;
        }

        omTxt.setFillPaint(foreColor);
        omTxt.setFont(new Font(fontName.substring(1, fontName.length() - 1), fontStyle, 10));
    }

    /*
     * Creates a tokenizer for each line of input
     */
    private StringTokenizer getTokens(BufferedReader br) throws IOException {
        String line;
        WHILE: while ((line = br.readLine()) != null) {

            if (line.equals(""))
                continue WHILE; // skip blank lines

            // should return the tokenizer as soon as we have a line
            return new StringTokenizer(line, " \t\n\r\f,()");
        }
        return null;
    }

    /*
     * Utility for doing case independant string comparisons... it's
     * neater this way
     */
    private boolean isSame(String str1, String str2) {
        if (str1.equalsIgnoreCase(str2)) {
            return true;
        } else {
            return false;
        }
    }

    /*
     * Converts MIF file color to Java Color object
     */
    private Color convertColor(int val) {
        int red = 0;
        int green = 0;
        int blue = 0;
        int rem = val;
        if (rem >= 65536) {
            red = rem / 65536;
            rem = rem - red * 65536;
        }
        if (rem >= 255) {
            green = rem / 256;
            rem = rem - green * 256;
        }
        if (rem > 0)
            blue = rem;

        return new Color(red, green, blue);
    }
}

/* Last line of file */ 
TOP

Related Classes of com.bbn.openmap.layer.mif.MIFLoader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.