Task 402: .MML File Format

Task 402: .MML File Format

1. List of all the properties of this file format intrinsic to its file system

The .MML file format refers to Map Markup Language, used in CartoCSS (a styling language for Mapnik-based mapping applications). It is a text-based format that can be structured as either YAML or JSON, defining map layers, styles, projections, and other configurations for generating maps. The properties are key-value pairs that describe the map's global settings, layers, data sources, and styles. Below is a complete list of properties, grouped by scope, including their types and descriptions (based on the official CartoCSS documentation). Sub-properties are indented.

Top-Level Properties

  • center: Array - Specifies the initial center coordinates and zoom level of the map (e.g., [longitude, latitude, zoom]).
  • bounds: Array - Defines a bounding box of the map extent (e.g., [min_longitude, min_latitude, max_longitude, max_latitude]).
  • description: String - A description for the stylesheet.
  • format: Keyword (e.g., png, jpeg, tiff, webp) - Specifies the output image format.
  • Layer: Array - Lists all layers in the project; each is an object defining a map layer (order determines drawing order). See Layer Properties below for sub-properties.
  • maxzoom: Integer - Maximum zoom level of the map.
  • metatile: Integer - Number of tiles per side of a metatile (e.g., 2 for a 2x2 metatile).
  • minzoom: Integer - Minimum zoom level of the map.
  • name: String - Name for the stylesheet.
  • _properties: Object - Global properties for layers (used when layers are referenced externally). Sub-properties mirror Layer > properties.
  • scale: Integer - Pixel scaling for output (e.g., 2 for double resolution).
  • srs: String - Projection in PROJ.4 format (Spatial Reference System).
  • Stylesheet: Array - Lists styles or style files; can reference MSS files or define style objects directly. See Stylesheet Properties below for sub-properties.

Layer Properties (within each Layer object)

  • class: String - Classes for the layer (space-separated; used in style selectors).
  • Datasource: Object - Specifies the data source. See Datasource Properties below for sub-properties.
  • extent: Array - Bounding box for the layer (e.g., [min_longitude, min_latitude, max_longitude, max_latitude]).
  • geometry: Keyword (e.g., linestring, point, polygon, raster) - Geometry type for the datasource.
  • id: String - Unique identifier for the layer (used in style selectors).
  • properties: Object - Attributes for the Mapnik layer.
  • abstract: String - Short description of the layer.
  • group-by: String - Field for grouped rendering.
  • maxzoom: Integer - Zoom level until which the layer is visible.
  • minzoom: Integer - Zoom level from which the layer is visible.
  • status: Integer (0 or 1) - Whether the layer is active (1) or inactive (0).
  • title: String - Title of the layer.
  • srs: String - Projection for the layer in PROJ.4 format.
  • srs-name: String - Name of the SRS.

Datasource Properties (within Datasource object)

  • type: Keyword (e.g., shape, postgis, pgraster, raster, gdal, ogr, osm) - Format of the data source.
  • Type-specific sub-properties:
  • band (for gdal, pgraster): Integer - Raster band index (1-based; -1 or 0 for all bands).
  • dbname (for postgis, pgraster): String - PostgreSQL database name.
  • encoding (for ogr, postgis, shape): String - Encoding (e.g., utf-8, latin1).
  • extent (for ogr, postgis, pgraster): String - Maximum extent (format: min_lon,min_lat max_lon,max_lat).
  • file (for gdal, ogr, osm, raster, shape): String - Path and filename of the data source.
  • geometry_field (for postgis): String - Column name for geometry.
  • host (for postgis, pgraster): String - PostgreSQL hostname.
  • layer (for ogr): String - Layer name to display.
  • layer_by_index (for ogr): Integer - Layer index (if no name specified).
  • layer_by_sql (for ogr): String - SQL statement for the datasource.
  • password (for postgis, pgraster): String - PostgreSQL password.
  • port (for postgis, pgraster): String - PostgreSQL port.
  • raster_field (for pgraster): String - Column name for raster data.
  • simplify_geometries (for postgis): Boolean - Whether to reduce input vertices.
  • table (for postgis, pgraster): String - Table name or subquery.
  • user (for postgis, pgraster): String - PostgreSQL username.

Stylesheet Properties (within each Stylesheet object, if defined directly)

  • id: String - Identifier for the style (often the filename).
  • data: String - Actual style content.

These properties define the intrinsic structure of .MML files, which are parsed to generate Mapnik XML for rendering maps.

3. Write a ghost blog embedded html javascript that allows a user to drag n drop a file of format .MML and it will dump to screen all these properties

MML File Parser

Drag and Drop .MML File Parser

Drag and drop a .MML file here

4. Write a python class that can open any file of format .MML and decode read and write and print to console all the properties from the above list

import json
import yaml  # Requires PyYAML: pip install pyyaml

class MMLHandler:
    def __init__(self, filepath):
        self.filepath = filepath
        self.data = None

    def read(self):
        with open(self.filepath, 'r') as f:
            content = f.read()
            try:
                # Try YAML parse (since YAML is superset of JSON)
                self.data = yaml.safe_load(content)
            except yaml.YAMLError:
                try:
                    self.data = json.loads(content)
                except json.JSONDecodeError as e:
                    raise ValueError(f"Invalid .MML file: {e}")

    def print_properties(self):
        if not self.data:
            print("No data loaded. Call read() first.")
            return

        def dump(obj, prefix=''):
            for key, value in obj.items():
                print(f"{prefix}{key}: {value if not isinstance(value, (dict, list)) else ''}")
                if isinstance(value, (dict, list)):
                    dump(value if isinstance(value, dict) else {i: v for i, v in enumerate(value)}, prefix + '  ')

        dump(self.data)

    def write(self, new_filepath=None):
        if not self.data:
            print("No data to write. Call read() first.")
            return
        filepath = new_filepath or self.filepath
        with open(filepath, 'w') as f:
            yaml.dump(self.data, f, default_flow_style=False)

# Example usage:
# handler = MMLHandler('example.mml')
# handler.read()
# handler.print_properties()
# handler.write('output.mml')

5. Write a java class that can open any file of format .MML and decode read and write and print to console all the properties from the above list

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Iterator;

public class MMLHandler {
    private String filepath;
    private JsonNode data;

    public MMLHandler(String filepath) {
        this.filepath = filepath;
    }

    public void read() throws IOException {
        String content = new String(Files.readAllBytes(Paths.get(filepath)));
        ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); // Tries YAML first
        try {
            data = mapper.readTree(content);
        } catch (IOException e) {
            mapper = new ObjectMapper(); // Fallback to JSON
            data = mapper.readTree(content);
        }
    }

    public void printProperties() {
        if (data == null) {
            System.out.println("No data loaded. Call read() first.");
            return;
        }
        dump(data, "");
    }

    private void dump(JsonNode node, String prefix) {
        if (node.isObject()) {
            Iterator<String> fields = node.fieldNames();
            while (fields.hasNext()) {
                String key = fields.next();
                JsonNode value = node.get(key);
                System.out.println(prefix + key + ": " + (value.isContainerNode() ? "" : value.asText()));
                if (value.isContainerNode()) {
                    dump(value, prefix + "  ");
                }
            }
        } else if (node.isArray()) {
            for (int i = 0; i < node.size(); i++) {
                System.out.println(prefix + "[" + i + "]: ");
                dump(node.get(i), prefix + "  ");
            }
        }
    }

    public void write(String newFilepath) throws IOException {
        if (data == null) {
            System.out.println("No data to write. Call read() first.");
            return;
        }
        ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
        mapper.writeValue(new File(newFilepath != null ? newFilepath : filepath), data);
    }

    // Example usage:
    // public static void main(String[] args) throws IOException {
    //     MMLHandler handler = new MMLHandler("example.mml");
    //     handler.read();
    //     handler.printProperties();
    //     handler.write("output.mml");
    // }
}

(Note: Requires Jackson libraries: jackson-databind, jackson-dataformat-yaml.)

6. Write a javascript class that can open any file of format .MML and decode read and write and print to console all the properties from the above list

const fs = require('fs'); // For Node.js
const yaml = require('js-yaml'); // Requires js-yaml: npm install js-yaml

class MMLHandler {
    constructor(filepath) {
        this.filepath = filepath;
        this.data = null;
    }

    read() {
        const content = fs.readFileSync(this.filepath, 'utf8');
        try {
            this.data = yaml.load(content); // YAML first
        } catch (e) {
            try {
                this.data = JSON.parse(content); // Fallback to JSON
            } catch (err) {
                throw new Error(`Invalid .MML file: ${err.message}`);
            }
        }
    }

    printProperties() {
        if (!this.data) {
            console.log('No data loaded. Call read() first.');
            return;
        }

        function dump(obj, prefix = '') {
            for (const key in obj) {
                console.log(`${prefix}${key}: ${typeof obj[key] !== 'object' || obj[key] === null ? obj[key] : ''}`);
                if (typeof obj[key] === 'object' && obj[key] !== null) {
                    dump(obj[key], prefix + '  ');
                }
            }
        }

        dump(this.data);
    }

    write(newFilepath = null) {
        if (!this.data) {
            console.log('No data to write. Call read() first.');
            return;
        }
        const output = yaml.dump(this.data);
        fs.writeFileSync(newFilepath || this.filepath, output, 'utf8');
    }
}

// Example usage:
// const handler = new MMLHandler('example.mml');
// handler.read();
// handler.printProperties();
// handler.write('output.mml');

7. Write a c class that can open any file of format .MML and decode read and write and print to console all the properties from the above list

#include <iostream>
#include <fstream>
#include <string>
#include <nlohmann/json.hpp> // Requires nlohmann/json (single-header JSON library); assumes JSON format for simplicity (YAML parsing requires additional lib like yaml-cpp)

using json = nlohmann::json;

class MMLHandler {
private:
    std::string filepath;
    json data;

public:
    MMLHandler(const std::string& fp) : filepath(fp) {}

    void read() {
        std::ifstream file(filepath);
        if (!file.is_open()) {
            std::cerr << "Failed to open file." << std::endl;
            return;
        }
        std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
        try {
            data = json::parse(content); // Assumes JSON; for YAML, use yaml-cpp library
        } catch (json::parse_error& e) {
            std::cerr << "Invalid .MML file: " << e.what() << std::endl;
        }
    }

    void printProperties() {
        if (data.empty()) {
            std::cout << "No data loaded. Call read() first." << std::endl;
            return;
        }
        dump(data, "");
    }

private:
    void dump(const json& node, const std::string& prefix) {
        if (node.is_object()) {
            for (auto& el : node.items()) {
                std::cout << prefix << el.key() << ": " << (el.value().is_structured() ? "" : el.value().dump()) << std::endl;
                if (el.value().is_structured()) {
                    dump(el.value(), prefix + "  ");
                }
            }
        } else if (node.is_array()) {
            for (size_t i = 0; i < node.size(); ++i) {
                std::cout << prefix << "[" << i << "]: " << std::endl;
                dump(node[i], prefix + "  ");
            }
        }
    }

public:
    void write(const std::string& newFilepath = "") {
        if (data.empty()) {
            std::cout << "No data to write. Call read() first." << std::endl;
            return;
        }
        std::ofstream out(newFilepath.empty() ? filepath : newFilepath);
        out << data.dump(2); // Pretty-print with indent 2
    }
};

// Example usage:
// int main() {
//     MMLHandler handler("example.mml");
//     handler.read();
//     handler.printProperties();
//     handler.write("output.mml");
//     return 0;
// }

(Note: Assumes JSON format for simplicity; for YAML support, integrate yaml-cpp library. Requires nlohmann/json.hpp header.)