4

I have a Vue application that is using highchart with the vue-highcart component to plot maps. I need to plot points on this maps based on the Latitude and Longitude and, according to Highmaps documentation, I have to use the Proj4js. In plain old javascript I would simply use a <script> to get it into my page but I can't figure out how to do it ES6-way. I have tried the following:

import Proj4 from 'proj4';
import Highcharts from 'highcharts';
import VueHighcharts from 'vue-highcharts';
import LoadHighmaps from 'highcharts/modules/map';

LoadHighmaps(Highcharts);
Vue.use(VueHighcharts, { Proj4, Highcharts })

// ....

<highmaps :options="options"></highmaps>

And it doesnt work. It fails silently. The map is plotted but no point is plotted on the map. It is the same behaviour you get if you remmove the Proj4Js on this fiddle: http://jsfiddle.net/r9hugxsu/

Any help? Thanks in advance.

Edit: I am aware that I can simply put the script tag on my index.html. But I was just wondering if there is an ES6-way to do this kind of "dependency-script-including".

João Menighin
  • 3,083
  • 6
  • 38
  • 80
  • Did you `npm install proj4`? (And all others, anyway?) – acdcjunior Feb 27 '18 at 13:17
  • Yes yes. On my `package.json` I have `"proj4": "^2.4.4"`. Do you think it should work with that import? If I use the `script` tag on my HTML, before my bundled vue application of course, it works. hmm – João Menighin Feb 27 '18 at 13:27
  • Theoretically, without actually knowing that lib, that import could work, yes. Is `proj4` a regular (not dev) dependency? Also, could you try `import * as Proj4 from 'proj4';` ? – acdcjunior Feb 27 '18 at 13:31
  • Proj4 is a regular dependency, yes. And the `import *` didnt work as well. This may make no sense but when I include it as `script` then I have a global object called `proj4` (which is a function). I imagine somewhere in highmaps it is calling `proj4(foobar)`. But when I import it as ES6, if I understand right, it is not imported to global. So highmaps would have to use `Proj4.proj4(foobar)` and thats where it fails. Does this make any sense? – João Menighin Feb 27 '18 at 14:13
  • I tried `import {proj4} from 'proj4'` and it didnt work as well T_T – João Menighin Feb 27 '18 at 14:15
  • Kind of makes sense, yeah. Try `import * as proj4 from...`, then. Lowercase p. – acdcjunior Feb 27 '18 at 14:15
  • I tried, didnt work. This way it would still be access through proj4.proj4(foobar), no? – João Menighin Feb 27 '18 at 14:29
  • Yeah, I think it would. Look, do that `import * as Proj4...` and then do `console.log(Proj4)` to get a grip of what is being imported. Then remove the import and add the ` – acdcjunior Feb 27 '18 at 15:08
  • Good idea. The results doesnt help a lot, tough: https://ibb.co/fLMFLx It seems to be the same function (one minified). I also tried `import * as proj4 from 'proj4';` and the only difference is that the same function above goes inside a `default` object key. So: `proj4: { default: function(a, b, c)} }`. Any other ideas? I will try to reproduce this in some fiddle somehow... – João Menighin Feb 27 '18 at 16:07
  • If `import * as proj4 from 'proj4';` has a `default` key, then I think the correct way really is `import Proj4 from 'proj4';` (which `Proj4` will have the default property value). I honestly don't know. Do you change nothing else at all? Just remove the import and add the ` – acdcjunior Feb 27 '18 at 16:14

1 Answers1

9

I ran into this myself just recently. If you did manage to resolve this yourself already, then I'll at least leave this here as a quick answer to others running into this.

There doesn't seem to be documentation from Highsoft on this particular part, yet. I should probably file a docs issue with them.

Here's how I was able to resolve it in a React project I'm on.

Step 1: Create a wrapper import for proj4 that puts it on `window`

I made a separate "import wrapper" which, when I import it from anything that requires proj4 (which is just Highcharts) it imports the real proj4 from node_modules and sticks it on window as window.proj4. This is what Highmaps looks for when it's trying to find proj4.

For the sake of example, pretend this file is in @/maps/lib/proj4.js.

import proj4 from 'proj4';

// NOTE: when dealing with server side rendering as we are, check for window before doing things with it.
// If you're not doing server side rendering, then you don't need this check and can just assign straight to window.
if (typeof window !== 'undefined') {
  window.proj4 = window.proj4 || proj4;
}

export default proj4;

Step 2: Download and Install Maps

One thing that initially confused me when trying to use Highmaps is that none of the examples seemed to work when I tried to reproduce them in the project I'm on. This turned out to be because Highmaps does not include any maps itself, rather we need to download and manually install them, usually from their map collection.

As an aside, if you look at their JS map files, you'll see they basically just assign the map directly like so: Highcharts.maps["custom/world"] = {"title":"World, Miller projection, medium resolution",...}. For the project I'm on, I just downloaded the JSON version and did the assignment myself. I placed the map file itself at @/maps/custom/world.geo.json.

I did this in another import wrapper type file, this time for Highcharts. This file is in @/maps/lib/Highcharts.js.

import './proj4';
import Highcharts from 'highcharts';
import HighchartsMap from 'highcharts/modules/map';

import customWorld from '@/maps/custom/world.geo.json';

// NOTE: Again, if doing server side rendering, check for window before bothering.
// Highcharts modules crash on the server.
if (typeof window !== 'undefined') {
  HighchartsMap(Highcharts);

  Highcharts.maps['custom/world'] = customWorld;
}

export default Highcharts;

Note above that I placed a local import before all the global imports, the import of @/maps/lib/proj4.js. While not strictly necessary, I did this to ensure that proj4 will always be installed before Highcharts is even imported, just in case.

Step 3: Import Highcharts From Our Import Wrapper

In the chart components, then, I can just import from our wrapper rather than the node_modules install.

import Highcharts from '@/maps/lib/Highcharts';

// do stuff with Highcharts itself...

Aside: Series and Data

Not sure why, but I had to always include a separate series for the map lines themselves.

{
  // ...
  series: [
    // Series for the map.
    {
      name: 'Countries',
      color: '#E0E0E0',
      enableMouseTracking: false,
      showInLegend: false,
      zIndex: 1,
    },
    // Series for the data.  Have to remember to add data later using chart.series[1]
    // rather than chart.series[0].
    {
      type: 'mapbubble',
      name: 'Live Activity',
      data: [],
      // TODO: Format for datum... point.datum.userId, etc.
      tooltip: {
        pointFormat: '{point.datum.userId}',
      },
      minSize: 4,
      maxSize: 24,
      zIndex: 2,
    },
  ],
  // ...
}
Joseph Sikorski
  • 715
  • 6
  • 14
  • I actually didn't had time to come to this yet n_n' Thanks for the answer! – João Menighin May 01 '18 at 14:25
  • Most welcome! Do let us know if you do get a chance to try this out and if it works! – Joseph Sikorski May 01 '18 at 17:27
  • 1
    Awesome! What a PITA to get that working properly. Thanks much, the docs on their site really assumes jQuery/vanilla js for most... Using all of the fancy module loaders causes so many issues :facepalm: – Andrew McOlash Nov 09 '19 at 01:15
  • at the risk of running foul of the stackoverflow gods, I'm going to post a thank you since I can't upvote your answer again. Thank you so much for this. – Michael Wiles May 18 '22 at 07:32