WSG84 format to geoJSON on different projections?

Using Plot to do some mapping work. I am using a vanilla ‘Albers-usa’ to plot some geographical data. I want to add a ‘base layer’ type projection of internal sales districts. We have our sales districts in WSG84 ‘multi polygon’ format and ChatGPT4 (paid version) gave me this helper function to translate it to geoJSON format. Using a plot.geo(testLayer,{stroke: ‘black’}) seems to work, but there is significant overlap among the polygons. I have concluded this is because the sales districts were meant for a flat projection and not an Albers projection. Is there a workaround to this? Or do I have to go with a non-Albers projection?

testLayer = {
geoData = {
  type: "FeatureCollection",
  features:, index) => {
    const coordinates = polygonString
      .replace("POLYGON((", "")
      .replace("))", "")
      .split(", ")
      .map(coord => coord.split(" ").map(Number));

    return {
      type: "Feature",
      properties: {
        ge_name: cus.district_code[index]
      geometry: {
        type: "Polygon",
        coordinates: [coordinates]
return geoData}

I asked what could be wrong with the code.
The use of Floats instead of rounding to integers sounds like it would help with the overlaps issue.

Here are a few potential issues I see in the provided code for converting from WSG84 to geoJSON:

  • The coordinates are split on whitespace, mapped to Numbers, but not parsed as floating point values. This could result in incorrect coordinate values. Better to use parseFloat() on each coordinate.
  • The coordinates are stored in a flat array, not nested arrays for polygons. geoJSON requires polygon rings to be nested arrays.
  • Polygon rings are assumed to be clockwise by convention in geoJSON. The code doesn’t enforce or validate this.
  • Properties like name are hardcoded rather than mapped from input data.
  • No validation of geoJSON output structure and values.

To address these:

  • Use parseFloat() on each split coordinate
  • Nest the ring arrays:
coordinates: [
    [x, y], [x, y], ...
  • Reverse coordinate order if needed to enforce clockwise rings
  • Map properties from input data
  • Validate geoJSON output, e.g. with geojson-validation package

Overall the logic seems valid but it could be made more robust with some tweaks like these. Let me know if any of those need more explanation!

And the code would look like this:

function convertToGeoJSON(cus) {

  const features =, index) => {

    const coords = polygonString  
      .replace(/POLYGON\(\((.*)\)\)/, '$1') 
      .split(", ")
      .map(coordPair => {
        const [lng, lat] = coordPair.split(" ");
        return [parseFloat(lng), parseFloat(lat)];

    // Ensure clockwise rings
    if (!isClockwise(coords[0])) {

    const feature = {
      type: "Feature",
      geometry: {
        type: "Polygon", 
        coordinates: [coords]
      properties: {
        name: cus.district_code[index]
    return feature;


  const geojson = {
    type: "FeatureCollection", 
    features: features

// Validate GeoJSON output
  if (geojsonValidation.valid(geojson)) {
    return geojson;
  } else {
    throw new Error("Invalid geoJSON");


function isClockwise(ring) {
  // Logic to calculate if coordinates are clockwise  
  return isClockwise; 

The key changes:

  • Parse floats
  • Nest coordinate rings
  • Reverse coords if needed to enforce clockwise
  • Map properties from input data
  • Validate geoJSON output


  • / - Starts the regex literal
  • POLYGON( - Matches the string “POLYGON(” literally
  • ( - Opens a capturing group
  • .* - Matches any character 0 or more times
  • ) - Closes the capturing group
  • ) - Matches the “)” character literally
  • / - Ends the regex literal

That regex:

The key part is the capturing group (.*) which will match and capture everything in between “POLYGON((” and “))”.

The second part ‘$1’ refers to the first captured group in the regex. So this is replacing the full match with just the contents inside the polygons.

For example:

"POLYGON((0 0, 1 1, 1 0, 0 0))"
  .replace(/POLYGON\\(\\((.*)\\)\\)/, '$1')

// Returns: "0 0, 1 1, 1 0, 0 0"

So in summary:

  • /POLYGON(/ matches the starting POLYGON( string
  • (.*) captures the coordinates
  • ) matches the ending )) string
  • $1 replaces with just the captured coordinates

This allows us to easily strip off the POLYGON(( and )) to get just the coordinate string.

This code is untested and AI’s often only hint at the right direction but often add a coding/debugging challenge

1 Like