Openlayers Cannot Read Property 'w' of Null
… where we investigate projections and view our map from an Arctic perspective.
Projections: different ways of looking at the world
In the last section we met the need to convert from longitude/breadth coordinates to some kind of what I'll very loosely telephone call "map units". This was necessary because nosotros needed to translate from units that humans are about familiar with (degrees longitude/latitude) to units that make sense on a 2D map. This is an important point: a map is a 2D representation of a 3D object (the earth).
It turns out that there are many means of representing our 3D world on a 2D surface, each with diverse advantages and disadvantages. To create a 2D map of the world, it's necessary to interpret every 3D location on the surface of the earth to a given location on the map. This process is called projection; the different second representations of the globe are called projections, considering they are generated by projecting a signal on the globe to a point in some 2nd space.
I of the most popular projections is Mercator, in item because it makes navigation easier, merely also because the surface of the earth is mapped to a Cartesian filigree and thus lines of abiding breadth are parallel with the x-axis and lines of constant longitude are parallel with the y-axis. Some other way of putting this is that north is ever "up" and south is ever "down"; westward is ever "left" and east is ever "right". This projection also uses metres every bit its unit, so that one can more easily measure distances in familiar units (rather than in, say, degrees breadth or longitude). These features of the Mercator projection make navigation in a ring around the equator much simpler and it is fairly intuituive, notwithstanding information technology means that the map is very distorted close to the poles: distances of a few metres in reality are "stretched" to several kilometres.
And then if nosotros're interested in a map of, say, the Arctic, what exercise we do? We use a Stereographic Projection, in particular one of the many Polar Stereographic Projections. In these cases, we have a map where the distortion at the pole is minimised, with the disadvantage of large distortions at the equator (merely hey, nosotros're not interested in the equator, so we can live with the baloney). A commonly used project for the Arctic is NSIDC Sea Water ice Polar Stereographic North, which nosotros'll migrate to slowly using our test suite as a guide.
Changing our focus to the Arctic
Let's choose a location in the Arctic upon which we can centre our map. A skillful candidate is Longyearbyen which is the largest settlement in the Svalbard archipeligo, is located north of the Chill circumvolve and is home to the world'due south northern-most university. It'south located at 78.217 Due north, 15.633 E, so allow'southward update our examination for the map centre to await to be here rather than in Prague; our exam will now look like this:
Running the tests (make test
) gives this output:
Basic map 1) should have a view centred on Longyearbyen 0 passing (5ms) 1 failing i) Basic map should accept a view centred on Longyearbyen: AssertionError: expected fourteen.439999999999998 to be shut to xv.533 +/- 0.000001 at Context.<anonymous> (exam/map-test.js:x:23) at processImmediate (internal/timers.js:456:21) at process.topLevelDomainCallback (domain.js:137:xv)
We expect this failure, since we oasis't updated the production lawmaking to centre on Longyearbyen all the same.
Update the eye
aspect of the view to match the coordinates for Longyearbyen in src/js/index.js
and re-run the tests. You lot should see that the tests laissez passer again.
Bones map ✓ should have a view centred on Longyearbyen one passing (5ms)
At present run brand build
to create the webpack parcel and reload the map in your web browser. Y'all should see something very similar to this:
We're now much more than confident that our code is doing what we intend it to practise. This is a adept point to commit the changes to the repository:
# mention what the changes were and why in the commit message $ git commit src/js/alphabetize.js examination/map-test.js
Refactor time!
Nosotros plan to have multiple layers in this awarding: one for the map and one for the image data. Calculation the side by side layer in the layers
array is going to make the code difficult to understand, so let's extract the OpenStreetmap TileLayer
into its own variable:
Re-running the tests shows that nosotros haven't broken anything by making this modify. Let's too extract the View
into its own variable:
Again, running the test suite shows that we haven't broken anything. Note that I've jumped the gun hither a bit past calling calling the View
variable arcticView
because we aren't all the same using an Arctic projection, but that'due south not such a bad transgression as we're heading in that direction anyway.
Now commit the changes:
# mention extraction of view and layer into own variables $ git commit src/js/index.js
Hrm, I'chiliad non happy with the map's proper name. Do y'all think your users will want the map to exist called "My Map"? No. Let's make this more descriptive by calling it "Arctic Sea-Water ice Concentration". Also, notice that the text in the title bar is "OpenLayers case". Users will probably retrieve this is a bit dodgy, so it as well needs a better name; how virtually "Arctic Ice Data"? These changes require the <h1>
and <title>
elements (respectively) to be fix correctly. Opening src/index.html
in an editor, we can set up the title easily:
withal, we notice that the "My Map" header is actually an <h2>
element even though there'south no preceding <h1>
element. That was naughty of us! Thus we need to change the tag proper name and its contents from
to
Now build the app with make build
and reload it in your browser. It should expect something similar this:
That's much better! Commit the changes and so that they don't get away from u.s.:
# mention update of app championship and principal header in commit bulletin $ git commit src/alphabetize.html
View from the tiptop of the world
To let OpenLayers know that nosotros want to use the NSIDC Sea Ice Polar Stereographic Due north project, we add together the projection
option to the View
constructor and pass in a Projection
object or by a string specifying the projection's EPSG codeone. By default, OpenLayers uses Spherical Mercator, also known as Pseudo Mercator or Spider web Mercator, considering it is unremarkably used for web-based maps.
OpenLayers has several projections that information technology already knows about, unfortunately, epsg:3413
isn't 1 of them. Yous can test this by adding the projection
option to the View
constructor:
The test suite should fail with an error message similar this:
/path/to/arctic-sea-ice-map/node_modules/ol/View.js:1 TypeError: Cannot read belongings 'getExtent' of null at createResolutionConstraint (/path/to/arctic-sea-ice-map/node_modules/ol/View.js:1509:33) at View.require.View.applyOptions_ (/path/to/arctic-bounding main-ice-map/node_modules/ol/View.js:338:40) at new View (/path/to/chill-sea-ice-map/node_modules/ol/View.js:326:fifteen)
The error Cannot read property 'getExtent' of zero
means that the view can't get the extent of a nada projection and hence the test fails. If you build the project (make build
) and reload the app in your browser, y'all'll see that the app stops working.
Remove this change with
$ git checkout src/js/index.js
and then that we go dorsum to a clean state before going further.
The solution to the problem of the missing projection definition is to define the projection ourselves, and to do this we need to utilise the proj4js library. Install the library via npm
:
Now we need to import the proj4
library into our app also equally the register
function from the OpenLayers proj4
library, so that nosotros tin tell OpenLayers well-nigh whatsoever new projections we define. Add these lines to the list of import statements in src/js/index.js
:
At present we can define the 'EPSG:3413' projection using its PROJ4 definition (encounter, e.g. the proj4js definition for the projection on epsg.io):
(this code can come afterward the import
statements). At present we simply need to let OpenLayers know about this new PROJ4 definition:
This time, if we add the line
to the View
constructor, we'll find that the tests pass.
Withal, if we build the project via brand build
and view the app in dist/alphabetize.html
we'll find that the map is centred over Australia and Republic of indonesia and that these are shown upside downward. What's going on now? Remember how nosotros were using fromLonLat
and toLonLat
to convert longitudes and latitudes into map-based (projection-based) coordinates? Well, these functions presume the OpenLayers default project (EPSG:3857 a.k.a. Web Mercator) unless told otherwise. We thus need to explicitly specify the project when calling these functions in order to correctly translate longitude/breadth values into the appropriate projection coordinates.
Let'due south prepare the test first. Add the argument 'EPSG:3413'
to the toLonLat()
call within our test:
Running the test suite should at present barf horribly (but so, nosotros expect this to happen, and so it's not a bad affair).
Basic map 1) should accept a view centred on Longyearbyen 0 passing (9ms) ane failing i) Basic map should have a view centred on Longyearbyen: AssertionError: expected 128.14963323608137 to be close to fifteen.633 +/- 0.000001
A notation for those who have been watching closely: we didn't have to ascertain 'EPSG:3413'
within the test file considering it got added to the global namespace when nosotros imported the map
variable from the main package (in index.js
). This came every bit a bit of a surprise to me (peculiarly as someone used to other languages); I'd expected that I'd accept to ascertain the projection in the test file because I'one thousand not exporting it from the main package. Notwithstanding, information technology seems that JavaScript not only parses the code it imports, but executes it besides, and then at that place can be side furnishings from using an import
statement. This is expert to know and adept to keep in listen when developing JavaScript lawmaking that this tin can happen.
Returning to the main code, we now just demand to add the projection to the fromLonLat()
call:
The exam suite will now pass. Yay! Also, building the project and reloading the app in a browser will evidence that we're now viewing the Chill.
To bank check that we definitely are centred above Longyearbyen, zoom in using the +
push button in the elevation left-mitt corner. You should meet something like this:
which shows Svalbard and if y'all zoom in even further, you will find that it is, indeed, centred over Longyearbyen.
That's a nice result, and then let's commit this country to the repository:
# mention that we're using the new projection in the commit message and why $ git commit package-lock.json package.json src/ test/
Remove projection name duplication
Have you noticed that we've referenced the cord 'EPSG:3413'
several times? Not only is it adequately difficult to type, it's non skillful programming manner to direct refer to a string in several places; we should exist using a variable and then that we can test aspects of the projection definition, but besides so that we can avert typos in the cord and hence avoid having incorrectly defined projections being used in functions and constructors.
Let's define the project as a variable. To exercise this, nosotros need to import the get
function from the ol/proj
library:
and then, after the proj4
definitions have been registered, define the project as a variable:
Now we can replace the projection strings with this variable; the View
constructor looks thus:
Running the examination suite, you should run across the tests pass. However, if we make the same replacement in the test:
the tests will barf:
Basic map 1) should have a view centred on Longyearbyen 0 passing (9ms) one declining 1) Bones map should have a view centred on Longyearbyen: ReferenceError: epsg3413 is not defined at Context.<anonymous> (test/map-test.js:12:65) at process.topLevelDomainCallback (domain.js:120:23)
which is non a bad thing, because nosotros're no longer referencing (via a string) a projection which has been implicitly made available in the global application namespace. What nosotros can do now is extract the project definition into a module and nosotros can import only the things that nosotros need from it: in this case, the projection object.
Permit'due south accept a footstep back and undo the change nosotros made to the test file, so that we can get the tests passing once more:
$ git checkout test/map-examination.js
Now the tests pass and nosotros have a solid base to work from.
Our programme now is to extract the projection-related code into its ain module. Nosotros'll start by creating a test file for the projections module we desire to build. Open up the new file test/projections-exam.js
in your favourite editor, import the chai library and add together the test suite clarification:
1 of the simplest tests we can write would be to check if the projection's EPSG code matches that which we look: 'EPSG:3413'. This volition bootstrap the test suite for the projections module. Add together the post-obit test to the depict
block:
The tests now dice with this error message:
Bones map ✓ should have a view centred on Longyearbyen EPSG:3413 ane) should use EPSG:3413 equally its projection code 1 passing (16ms) 1 failing one) EPSG:3413 should use EPSG:3413 as its projection lawmaking: ReferenceError: epsg3413 is not defined at Context.<bearding> (test/projections-exam.js:5:5) at process.topLevelDomainCallback (domain.js:120:23)
The important thing to note is that the variable epsg3413
hasn't been defined. That'south something nosotros should import from the projections module nosotros want to build, then let's import that into the test file:
Of course the examination suite dies horribly:
/path/to/arctic-sea-ice-map/examination/projections-test.js:i Error: Cannot find module '../src/js/projections.js' Require stack: - /path/to/arctic-sea-ice-map/test/projections-test.js
because it can't notice the file we reference, so let's create that. Open the file src/js/projections.js
in your editor and add the post-obit code:
The tests withal fail (we didn't expect otherwise) but they get a scrap further (which we did expect):
file:///path/to/arctic-sea-ice-map/src/js/projections.js:1 export { epsg3413 as default }; ^^^^^^^^ SyntaxError: Export 'epsg3413' is not defined in module
Now move the lines
from src/js/index.js
into src/js/projections.js
(make sure to put this lawmaking above the export
statement that is already in the file). The tests will still die; this fourth dimension because index.js
doesn't know anything most the variable epsg3413
.
/path/to/arctic-sea-ice-map/src/js/index.js:1 ReferenceError: epsg3413 is non divers
Let's import it to get rid of the fault. Add together the line
to the end of the listing of imports in src/js/index.js
. Now the tests pass!
Basic map ✓ should take a view centred on Longyearbyen EPSG:3413 ✓ should use EPSG:3413 as its projection code two passing (8ms)
Now nosotros tin can apply our new module in the map tests. Add together the following line later on map has been imported into test/map-test.js
:
and replace 'EPSG:3413'
with the variable epsg3413
. The tests still pass! Bully!
This is a proficient place to commit our progress to the repository:
$ git add src/js/projections.js test/projections-test.js # mention extraction of projection into module and reduction of lawmaking # duplication in the commit message $ git commit src/ test/
Focussing more on the Arctic
Did you notice that the zoomed-out view of the Chill (mentioned above) showed quite a lot of the world? It even showed parts of Europe, Africa, the Middle Eastward and North America which won't ever accept sea-ice. Let's be more focussed on the Chill and limit the map view to a more specific area. Since this is an aspect of the projection object we'll demand to extend the tests for the projections module. Open test/projections-exam.js
and add together the following test to the main describe
block:
where the extent is divers past the extremal lower-left and upper-right coordinates in the NSIDC Body of water Ice Polar Stereographic North projection.
Equally expected, the tests fail because we haven't all the same divers the extent for the project:
Basic map ✓ should take a view centred on Longyearbyen EPSG:3413 ✓ should use EPSG:3413 as its projection code 1) should utilize the NSIDC north pole extent ii passing (11ms) 1 declining 1) EPSG:3413 should employ the NSIDC north pole extent: TypeError: Cannot read belongings 'should' of null
The error message Cannot read property 'should' of nada
tells us that the output of getWorldExtent()
in the test returned null
. If we now set the map and world extents in the projections module:
we find that the tests laissez passer. Cool! To restrict the map view to the newly-divers extent, we laissez passer the extent
parameter to the View
constructor.
The extent that OpenLayers uses for the view depends upon the zoom level and the resolution at which the map should be displayed, hence information technology's not sensible to test for the view's extent because this number isn't a constant. Withal, to see that the map behaves as we intend, build the projection (make build
), load the app in a browser and zoom out to the minimum zoom level. Panning effectually you can see that the maximum extent for the map view is now much smaller than it was before.
This is a good place to save the state of the project to the repository:
# mention that we're setting the projection and view extents explicitly (and # why) in the commit bulletin $ git commit src/ examination/
Condign more specific to Svalbard
Let'southward make the map more than specific to Svalbard by increasing the zoom level and rotating the map so that Svalbard has information technology's typical "inverted triangle" shape which we are most used to when viewing Svalbard on e.g. a Mercator projection. We thus want to increase the zoom level to six and rotate the map past 45 degrees. We tin put these requirements into tests and thus ensure that they are met.
Start, let's set the zoom level. Add this examination to the map-examination.js
file:
The tests will fail considering we currently have a zoom level of 4. Set this value to 6 and run the tests once again:
Yay, the tests are passing. Commit the change to the repo:
# mention setting default zoom level in commit message $ git commit src/js/alphabetize.js test/map-test.js
Now allow'southward set the default rotation by adding this test to map-test.js
:
Notation that angles have to exist in radians, hence the cistron of π/180.
Of course the tests fail:
ane) Basic map should have a default rotation of 45 degrees: AssertionError: expected 0 to equal 0.7853981633974483 + expected - actual -0 +0.7853981633974483
Now fix the rotation
property in the View
constructor:
Running the tests again shows that the examination suite passes.
Bones map ✓ should have a view centred on Longyearbyen ✓ should have a default zoom of 6 ✓ should have a default rotation of 45 degrees EPSG:3413 ✓ should use EPSG:3413 as its projection code ✓ should apply the NSIDC north pole max sea ice extent five passing (10ms)
Building the app (make build
) and opening it in a browser, y'all should see output similar to this image:
Nice! This is what we want to see
Commit this change every bit well:
# mention that we're setting the default rotation in the commit bulletin $ git commit src/js/index.js test/map-test.js
Epitomize
We now have a ameliorate idea of what projections are and how we tin use them to control how a map is visualised. We've likewise been able to blast down more than details of the awarding and what nosotros wait it to expect like and acquit by building a minor tests then getting them to work. Therefore, slowly, bit by bit, nosotros're edifice an awarding on a foundation which is condign more firm with every test. This office also involved a lot of work; peradventure information technology's time you went to fetch a cup of tea or coffee before we dive into visualising Arctic sea-ice concentration data.
Source: https://ptc-it.de/developing-openlayers-apps-in-es6-mocha-karma-webpack-projections/
0 Response to "Openlayers Cannot Read Property 'w' of Null"
إرسال تعليق