fossasia/loklak_webclient

View on GitHub
iframely/plugins/domains/openstreetmap.org.js

Summary

Maintainability
C
1 day
Test Coverage
var URL = require("url");
var QueryString = require("querystring");

var LayerMap = {
    M: 'mapnik',
    O: 'mapnik', // osma render is no longer supported
    C: 'cyclemap',
    T: 'transportmap',
    Q: 'mapquest'
};

var ReverseLayerMap = {
    mapnik:       'M',
    cyclemap:     'C',
    transportmap: 'T',
    mapquest:     'Q'
};

function sinh(x) {
    return (Math.exp(x) - Math.exp(-x)) / 2;
}

// See: http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Lon..2Flat._to_bbox
function getTileNumber(lat, lon, zoom) {
    var n = Math.pow(2, zoom);
    var xtile = Math.floor((lon + 180) / 360 * n);
    var ytile = (Math.floor((1 - Math.log(Math.tan(lat * Math.PI / 180) +
                1 / Math.cos(lat * Math.PI / 180)) / Math.PI) / 2 * n));
    return [xtile, ytile];
}

function getLonLat(xtile, ytile, zoom) {
    var n = Math.pow(2, zoom);
    var lon = xtile / n * 360 - 180;
    var lat = Math.atan(sinh(Math.PI * (1 - 2 * ytile / n))) / Math.PI * 180;
    return [lon, lat];
}

function getBBox(lat, lon, zoom, width, height) {
    var tile_size = 256;
    var xytile = getTileNumber(lat, lon, zoom);
    var xtile = xytile[0];
    var ytile = xytile[1];

    var xtile_s = (xtile * tile_size - width  / 2) / tile_size;
    var ytile_s = (ytile * tile_size - height / 2) / tile_size;
    var xtile_e = (xtile * tile_size + width  / 2) / tile_size;
    var ytile_e = (ytile * tile_size + height / 2) / tile_size;

    var lonlat_s = getLonLat(xtile_s, ytile_s, zoom);
    var lonlat_e = getLonLat(xtile_e, ytile_e, zoom);

    return [lonlat_s[0], lonlat_s[1], lonlat_e[0], lonlat_e[1]];
}

module.exports = {

    re: /^https?:\/\/(?:www\.)?openstreetmap\.org\/(?:\?.+|\#.*map=.+|export\/embed\.html\?)/i,

    mixins: [
        'html-title',
        'favicon'
    ],

    getLink: function(url) {
        // Currently the notes and data layers aren't used in embeds and static maps,
        // but who knows, maybe that'll change?
        var layers, layer, zoom, lat, lon, bbox, marker, notes = false, data = false;

        url = URL.parse(url, true);

        // parse query string
        var query = url.query;

        layer  = query.layer;
        layers = query.layers;
        zoom   = Number(query.zoom);

        // lat & lon
        if ('lat' in query) { lat = Number(query.lat); }
        if ('lon' in query) { lon = Number(query.lon); }

        // bounding box
        if ('minlat' in query && 'minlon' in query && 'maxlat' in query && 'maxlon' in query) {
            bbox = [Number(query.minlon), Number(query.minlat),
                    Number(query.maxlon), Number(query.maxlat)];
        }
        else if ('bbox' in query) {
            bbox = query.bbox.split(',',4).map(Number);
        }

        if (bbox && bbox.some(isNaN)) {
            bbox = undefined;
        }

        // marker
        if ('mlon' in query && 'mlat' in query) {
            marker = [Number(query.mlat), Number(query.mlon)];
        }

        // parse hash
        query = QueryString.parse((url.hash||'').replace(/^#/,''));

        if (query.map) {
            if ('layers' in query) {
                layers = query.layers;
            }

            // zoom, lat & lon
            var map = query.map.split('/');
            zoom = Number(map[0]);
            lat  = Number(map[1]);
            lon  = Number(map[2]);

            if (!isNaN(lat) && !isNaN(lon)) {
                // hash overwrites query
                bbox = undefined;
            }
        }

        // lat & lon from marker if not otherwise defined
        if (isNaN(lat) || isNaN(lon)) {
            if (bbox) {
                // XXX: this might fail when bbox spans over coordinate system boundaries
                lon = (bbox[0] + bbox[2]) * 0.5;
                lat = (bbox[1] + bbox[3]) * 0.5;
            }

            if (marker) {
                lat = marker[0];
                lon = marker[1];
            }
        }

        if (isNaN(lat) || isNaN(lon)) {
            return [];
        }

        var embed_width  = 640;
        var embed_height = 480;

        // musst be > 40x40 to work
        var thumb_width  = 320;
        var thumb_height = 240;

        // parse extra layers
        if (layers) {
            if (layers.indexOf('N') >= 0) {
                notes  = true;
                layers = layers.replace(/N/g,'');
            }

            if (layers.indexOf('D') >= 0) {
                data   = true;
                layers = layers.replace(/D/g,'');
            }
        }

        if (layer && ReverseLayerMap.hasOwnProperty(layer)) {
            // ok
        }
        else if (layers && LayerMap.hasOwnProperty(layers)) {
            layer = LayerMap[layers];
        }
        else {
            layer = 'mapnik';
        }

        zoom = isNaN(zoom) ? 10 : Math.floor(zoom);
        if (zoom < 0) {
            zoom =  0;
        } else if (zoom > 18) {
            zoom = 18;
        }

        if (!bbox) {
            bbox = getBBox(lat, lon, zoom, embed_width, embed_height);
        }

        // don't use QueryString.stringify here because OpenStreetMap can't
        // cope with "," encoded as "%2C"
        var embed_url = "//www.openstreetmap.org/export/embed.html?bbox="+
                         bbox.join(',')+'&layer='+layer;

        var thumb_query = {
            show:  '1',
            fmt:   'png',
            layer: 'mapnik', // the only layer that seems to work currently
            lon:   lon,
            lat:   lat,
            z:     Math.max(0,zoom-1), // zoom out a bit for thumbnail
            w:     thumb_width,
            h:     thumb_height,
            att:   'none'
        };

        if (marker) {
            embed_url += '&marker='+marker.join(',');
            thumb_query.mlat0 = marker[0];
            thumb_query.mlon0 = marker[1];
        }

        return [{
            href: embed_url,
            rel:  CONFIG.R.app,
            type: CONFIG.T.text_html,
            "aspect-ratio": embed_width / embed_height
        }
        // Unfortunatelly, the do not provide StaticMaps any longer. Maybe one day. 
        // https://help.openstreetmap.org/questions/26293/error-on-image-stream-using-ojwdevopenstreetmaporg
        /*
        , {
            href: "http://ojw.dev.openstreetmap.org/StaticMap/?"+QueryString.stringify(thumb_query),
            rel:  CONFIG.R.thumbnail,
            type: CONFIG.T.image_png,
            width:  thumb_width,
            height: thumb_height
        }*/
        ];
    },

    tests: [
        "http://www.openstreetmap.org/?lat=48.12446&lon=16.42282&zoom=15&layers=M",
        "http://www.openstreetmap.org/?lat=51.5064&lon=-0.1281&zoom=14&layers=MC",
        "http://www.openstreetmap.org/#map=12/50.2598/28.6695",
        {
            noFeeds: true
        }
    ]
};