Converting Google Maps styles array to Google Static Maps styles string
Asked Answered
C

6

21

I've built a tool that allows folks to apply a JSON map style to a Google Map, and am now wanting to add support for the Google Static Maps API.

Using the following styles array:

"[{"stylers":[]},{"featureType":"water","stylers":[{"color":"#d2dce6"}]},{"featureType":"administrative.country","elementType":"geometry","stylers":[{"weight":1},{"color":"#d5858f"}]},{"featureType":"administrative.country","elementType":"labels.text.fill","stylers":[{"color":"#555555"}]},{"featureType":"administrative","elementType":"geometry.stroke","stylers":[{"visibility":"off"}]},{"featureType":"administrative.country","stylers":[{"visibility":"on"}]},{"featureType":"road.highway","stylers":[{"saturation":-100},{"lightness":40},{"visibility":"simplified"}]},{"featureType":"road.arterial","stylers":[{"saturation":-100},{"lightness":40},{"visibility":"simplified"}]},{"featureType":"road.local","stylers":[{"saturation":-100},{"visibility":"simplified"}]},{"featureType":"landscape","elementType":"all","stylers":[{"hue":"#FFFFFF"},{"saturation":-100},{"lightness":100}]},{"featureType":"landscape.natural","elementType":"geometry","stylers":[{"saturation":-100}]},{"featureType":"landscape.man_made","elementType":"geometry.fill","stylers":[{"visibility":"simplified"},{"saturation":-100}]},{"featureType":"poi.park","elementType":"geometry","stylers":[{"saturation":-100},{"lightness":60}]},{"featureType":"poi","elementType":"geometry","stylers":[{"hue":"#FFFFFF"},{"saturation":-100},{"lightness":100},{"visibility":"off"}]}]"

(More documentation on format)

I need to ultimately convert this into a URLescaped string, in the format: style=feature:featureArgument|element:elementArgument|rule1:rule1Argument|rule2:rule2Argument (More documentation)

So far, I've written this JavaScript to try and convert things, but it isn't working properly:

  function get_static_style(styles) {
    var result = '';
    styles.forEach(function(v, i, a){
      if (v.stylers.length > 0) { // Needs to have a style rule to be valid.
        result += (v.hasOwnProperty('featureType') ? 'feature:' + v.featureType : 'feature:all') + '|';
        result += (v.hasOwnProperty('elementType') ? 'element:' + v.elementType : 'element:all') + '|';
        v.stylers.forEach(function(val, i, a){
          var propertyname = Object.keys(val)[0];
          var propertyval = new String(val[propertyname]).replace('#', '0x');
          result += propertyname + ':' + propertyval + '|';
        });
      }
    });
    console.log(result);
    return encodeURIComponent(result);
  }

Alas, this code is outputting the following:

Output

(Right-click and select "copy URL" to see the full path I'm using -- the above is direct from the static images API)

...When instead it should look like this:

London with style

Any ideas? Thanks!

Clothilde answered 1/10, 2013 at 11:40 Comment(0)
S
25

Each single style must be supplied with a separate style-parameter:

 function get_static_style(styles) {
    var result = [];
    styles.forEach(function(v, i, a){
      var style='';
      if (v.stylers.length > 0) { // Needs to have a style rule to be valid.
        style += (v.hasOwnProperty('featureType') ? 'feature:' + v.featureType : 'feature:all') + '|';
        style += (v.hasOwnProperty('elementType') ? 'element:' + v.elementType : 'element:all') + '|';
        v.stylers.forEach(function(val, i, a){
          var propertyname = Object.keys(val)[0];
          var propertyval = val[propertyname].toString().replace('#', '0x');
          style += propertyname + ':' + propertyval + '|';
        });
      }
      result.push('style='+encodeURIComponent(style))
    });

    return result.join('&');
  }

watch the result

Sequela answered 1/10, 2013 at 13:23 Comment(1)
Ah! That totally makes sense now. Thanks!Clothilde
C
12

The selected answer didn't work for me.
But only because I had a few objects without styler parameters.
I had to modify it like so:

function get_static_style(styles) {
    var result = [];
    styles.forEach(function(v, i, a){

        var style='';
        if( v.stylers ) { // only if there is a styler object
            if (v.stylers.length > 0) { // Needs to have a style rule to be valid.
                style += (v.hasOwnProperty('featureType') ? 'feature:' + v.featureType : 'feature:all') + '|';
                style += (v.hasOwnProperty('elementType') ? 'element:' + v.elementType : 'element:all') + '|';
                v.stylers.forEach(function(val, i, a){
                    var propertyname = Object.keys(val)[0];
                    var propertyval = val[propertyname].toString().replace('#', '0x');
                    // changed "new String()" based on: https://mcmap.net/q/492418/-what-is-the-jslint-approved-way-to-convert-a-number-to-a-string

                    style += propertyname + ':' + propertyval + '|';
                });
            }
        }
        result.push('style='+encodeURIComponent(style));
    });

    return result.join('&');
}

see it in action at: http://jsfiddle.net/ZnGpb/1/

p.s: JSHint

Compilation answered 26/2, 2014 at 10:51 Comment(1)
This is working well for me. A great function for the library.Aglaia
H
5

This is a PHP method that does the same thing

public function mapStylesUrlArgs($mapStyleJson)
{
    $params = [];

    foreach (json_decode($mapStyleJson, true) as $style) {
        $styleString = '';

        if (isset($style['stylers']) && count($style['stylers']) > 0) {
            $styleString .= (isset($style['featureType']) ? ('feature:' . $style['featureType']) : 'feature:all') . '|';
            $styleString .= (isset($style['elementType']) ? ('element:' . $style['elementType']) : 'element:all') . '|';

            foreach ($style['stylers'] as $styler) {
                $propertyname = array_keys($styler)[0];
                $propertyval = str_replace('#', '0x', $styler[$propertyname]);
                $styleString .= $propertyname . ':' . $propertyval . '|';
            }
        }

        $styleString = substr($styleString, 0, strlen($styleString) - 1);

        $params[] = 'style=' . $styleString;
    }

    return implode("&", $params);
}

src: https://gist.github.com/WouterDS/5942b891cdad4fc90f40

Hindemith answered 27/1, 2015 at 14:47 Comment(1)
Just what I needed. Thanks!Homs
C
1

Using simple map:

// Given styles array
const myStyles = [
{
  elementType: 'geometry',
  stylers: [
    {
      color: '#f5f5f5'
    }
  ]
},
{
  elementType: 'labels.icon',
  stylers: [
    {
      visibility: 'off'
    }
  ]
},
{
  elementType: 'labels.text.fill',
  stylers: [
    {
      color: '#616161'
    }
  ]
},
{
  elementType: 'labels.text.stroke',
  stylers: [
    {
      color: '#f5f5f5'
    }
  ]
},
{
  featureType: 'administrative',
  elementType: 'geometry',
  stylers: [
    {
      visibility: 'off'
    }
  ]
},
{
  featureType: 'administrative.land_parcel',
  elementType: 'labels.text.fill',
  stylers: [
    {
      color: '#bdbdbd'
    }
  ]
}];

const buildStyles = (styles) => {
  return styles.map((val, idx) => {
    const { featureType, elementType, stylers } = val;
    const feature = `feature:${featureType || 'all'}`;
    const element = `element:${elementType || 'all'}`;
    const styles = stylers.map(style => {
      const name = Object.keys(style)[0];
      const val = styles[name].replace('#', '0x');
      return `${name}:${val}`;
    });

    return `style=${encodeURIComponent(`${feature}|${element}|${styles}|`)}`;
  }).join('&');
};

const stylesStr = buildStyles(myStyles);
Cofsky answered 4/4, 2018 at 16:27 Comment(1)
Thanks for this. This solution worked for me apart from one small error: const val = styles[name].replace('#', '0x'); Should read: const val = style[name].replace('#', '0x');Accusative
O
0

I created this utility nodejs function for all Android developers.

Save the code below as flatten-mapstyle.js anyware.

Run using: node flatten-mapstyle.js /path/to/your/style/style_json.json

To urlencode output use -e flag, i.e: node flatten-mapstyle.js style_json.json -e

const fs = require('fs');
const {promisify} = require('util');

const args = process.argv.slice(2)

const filename = args[0]
const encode = args[1]

const exists = promisify(fs.exists);
const readFile = promisify(fs.readFile);


async function main() {
    try {
        if (filename == undefined || await !exists(filename)) {
            throw {
                'error': `file ${filename} does not exist`
            }
        }
        let json = await readFile(filename, 'utf8');
        console.log("=========COPY BELOW========")
        console.log(getStaticStyle(JSON.parse(json)))
        console.log("=========END OF COPY========")
    } catch (e) {
        console.error(e);
    }
}

main();

function getStaticStyle(styles) {
    var result = [];
    styles.forEach(function(v, i, a) {

        var style = '';
        if (v.stylers) { // only if there is a styler object
            if (v.stylers.length > 0) { // Needs to have a style rule to be valid.
                style += (v.hasOwnProperty('featureType') ? 'feature:' + v.featureType : 'feature:all') + '|';
                style += (v.hasOwnProperty('elementType') ? 'element:' + v.elementType : 'element:all') + '|';
                v.stylers.forEach(function(val, i, a) {
                    var propertyname = Object.keys(val)[0];
                    var propertyval = val[propertyname].toString().replace('#', '0x');
                    style += propertyname + ':' + propertyval + '|';
                });
            }
        }
        result.push('style=' + (encode == "-e" ? encodeURIComponent(style) : style));
    });

    return result.join('&');
}
Overhasty answered 29/12, 2017 at 9:13 Comment(0)
P
0

IMPROVED Javascript/Typescript version

For those who want to save some time:

I made some improvements to use it directly with a Javascript/Typescript function, enjoy !!! :)

static getStaticStyleParams(gmapJson: Record<string, any>[]) {
    function isColor(value: string) {
      return /^#[0-9a-f]{6}$/i.test(value.toString());
    }
    function toColor(value: string) {
      return `0x${value.slice(1)}`;
    }

    function parse() {
      const currentItems: string[] = [];
      const separator = '|';

      currentItems.length = 0;

      for (let i = 0; i < gmapJson.length; i++) {
        const item = gmapJson[i];
        const hasFeature = Object.prototype.hasOwnProperty.call(item, 'featureType');
        const hasElement = Object.prototype.hasOwnProperty.call(item, 'elementType');

        const stylers = item.stylers;
        let target = '';
        let style = '';

        if (!hasFeature && !hasElement) {
          target = 'feature:all';
        } else {
          if (hasFeature) {
            target = `feature:${item.featureType}`;
          }
          if (hasElement) {
            target = target ? target + separator : '';
            target += `element:${item.elementType}`;
          }
        }

        for (let s = 0; s < stylers.length; s++) {
          const styleItem = stylers[s],
            key = Object.keys(styleItem)[0]; // there is only one per element

          style = style ? style + separator : '';
          style += `${key}:${isColor(styleItem[key]) ? toColor(styleItem[key]) : styleItem[key]}`;
        }

        currentItems.push(target + separator + style);
      }

      return currentItems;
    }

    return `&style=${parse().join('&style=')}`;
  }
Phenylketonuria answered 22/1, 2023 at 17:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.