├── .gitignore
├── AspectAdaptive
├── UI.js
├── index.html
└── projection-selection-list.css
├── CanvasMap - readme.txt
├── CanvasMap
├── CanvasMap-min.js
├── CanvasMap.js
├── build.xml
├── compiler.jar
├── css
│ └── projection.css
├── jquery
│ ├── css
│ │ └── smoothness
│ │ │ ├── images
│ │ │ ├── animated-overlay.gif
│ │ │ ├── ui-bg_flat_0_aaaaaa_40x100.png
│ │ │ ├── ui-bg_flat_75_ffffff_40x100.png
│ │ │ ├── ui-bg_glass_55_fbf9ee_1x400.png
│ │ │ ├── ui-bg_glass_65_ffffff_1x400.png
│ │ │ ├── ui-bg_glass_75_dadada_1x400.png
│ │ │ ├── ui-bg_glass_75_e6e6e6_1x400.png
│ │ │ ├── ui-bg_glass_95_fef1ec_1x400.png
│ │ │ ├── ui-bg_highlight-soft_75_cccccc_1x100.png
│ │ │ ├── ui-icons_222222_256x240.png
│ │ │ ├── ui-icons_2e83ff_256x240.png
│ │ │ ├── ui-icons_454545_256x240.png
│ │ │ ├── ui-icons_888888_256x240.png
│ │ │ └── ui-icons_cd0a0a_256x240.png
│ │ │ ├── jquery-ui-1.10.2.custom.css
│ │ │ └── jquery-ui-1.10.2.custom.min.css
│ ├── jquery-1.11.1.min.js
│ ├── jquery-ui-1.10.2.custom.js
│ ├── jquery-ui-1.10.2.custom.min.js
│ └── jquery.ui.touch-punch.min.js
└── src
│ ├── Graticule.js
│ ├── Layer.js
│ ├── LineDrawer.js
│ ├── Map.js
│ ├── binarywrapper.js
│ ├── dbf.js
│ ├── lib
│ ├── binaryajax.js
│ └── excanvas.js
│ ├── projections
│ ├── AlbersConic.js
│ ├── Arden-Close.js
│ ├── AspectAdaptiveCylindrical.js
│ ├── Bonne.js
│ ├── Braun2.js
│ ├── Canters1.js
│ ├── Canters2.js
│ ├── CentralCylindrical.js
│ ├── CylindricalStereographic.js
│ ├── Eckert4.js
│ ├── Eckert6.js
│ ├── EqualAreaCylindrical.js
│ ├── Equirectangular.js
│ ├── Hammer.js
│ ├── Hufnagel.js
│ ├── Kavrayskiy1.js
│ ├── Kavrayskiy5.js
│ ├── KharchenkoShabanova.js
│ ├── LambertAzimuthalEqualAreaPolar.js
│ ├── LambertAzimuthalOblique.js
│ ├── LambertEqualAreaCylindrical.js
│ ├── McBrydeThomas1.js
│ ├── McBrydeThomas2.js
│ ├── Mercator.js
│ ├── Miller.js
│ ├── Miller2.js
│ ├── MillerPerspective.js
│ ├── MillerTransformation.js
│ ├── Mollweide.js
│ ├── NaturalEarth.js
│ ├── Pavlov.js
│ ├── PutninsP4P.js
│ ├── Robinson.js
│ ├── SineSeries.js
│ ├── Sinusoidal.js
│ ├── Strebe1995.js
│ ├── Tobler1.js
│ ├── Tobler2.js
│ ├── TransformableLambert.js
│ ├── TransverseTransformableLambert.js
│ ├── Urmayev2.js
│ ├── Urmayev3.js
│ ├── Wagner1.js
│ ├── Wagner4.js
│ ├── Wagner7.js
│ └── WagnerPseudocylindrical.js
│ └── shapefile.js
├── CentralMeridianSlider
├── UI.js
├── index.html
└── static.css
├── Hufnagel
├── UI.js
├── index.html
└── projection-selection-list.css
├── LambertTransition
├── UI.js
├── index.html
└── projection-selection-list.css
├── MillerTransformation
├── UI.js
├── index.html
└── static.css
├── ProjectionList
├── UI.js
└── index.html
├── README.md
├── SimpleMap
├── UI.js
└── index.html
├── SineSeries
├── UI.js
├── index.html
└── static.css
├── StrebeTransformation
├── UI.js
├── index.html
└── static.css
└── data
├── ne_110m_coastline.README.html
├── ne_110m_coastline.VERSION.txt
├── ne_110m_coastline.dbf
├── ne_110m_coastline.prj
├── ne_110m_coastline.shp
└── ne_110m_coastline.shx
/.gitignore:
--------------------------------------------------------------------------------
1 | # generic files to ignore
2 | *~
3 | *.lock
4 | *.DS_Store
5 | *.swp
6 | *.out
7 |
8 | # thumbnails
9 | ._*
10 |
11 | # files that might appear on external disk
12 | .Spotlight-V100
13 | .Trashes
14 |
15 | # experimental stuff
16 | /CylindricalAdaptive/
17 | /AlbersConic2LambertAzimuthal/
18 | /LambertTransformationTransverse/
--------------------------------------------------------------------------------
/AspectAdaptive/UI.js:
--------------------------------------------------------------------------------
1 | /*globals Layer, Graticule, $, AspectAdaptiveCylindrical, createMap */
2 |
3 | var map;
4 |
5 | function deselectButtons() {
6 | "use strict";
7 | $('#selectable .ui-selected').removeClass('ui-selected');
8 | $('#selectable .ui-selecting').removeClass('ui-selecting');
9 | }
10 |
11 | function updateProjection() {
12 | "use strict";
13 | var projection = new AspectAdaptiveCylindrical();
14 | projection.setAspectRatio($("#sliderAspect").slider("value") / 100);
15 | map.setProjection(projection);
16 | }
17 |
18 | function updateSliderTexts() {
19 | "use strict";
20 | var aspectRatio;
21 | aspectRatio = $("#sliderAspect").slider("value") / 100;
22 | $("#sliderAspectText").text(aspectRatio.toFixed(2));
23 | }
24 |
25 | function setProjection(selectedItem) {
26 | "use strict";
27 |
28 | var aspectRatio;
29 |
30 | switch (selectedItem) {
31 | case "projectionPlateCarree":
32 | aspectRatio = 0.5;
33 | break;
34 | case "projectionCompactMiller":
35 | aspectRatio = 0.6;
36 | break;
37 | }
38 |
39 | if (!isNaN(aspectRatio)) {
40 | $("#sliderAspect").slider("value", aspectRatio * 100);
41 | }
42 |
43 | updateSliderTexts();
44 | updateProjection();
45 | }
46 |
47 | $(function() {
48 | "use strict";
49 |
50 | var mousedown = false, firstRun = true, last_selectedItem_id;
51 | $('#selectable').selectable({
52 | start : function(event, ui) {
53 | mousedown = true;
54 | },
55 | stop : function(event, ui) {
56 | //Here the event ends, so that we can remove the selected class to all but the one we want
57 | $(event.target).children('.ui-selected').not("#" + last_selectedItem_id).removeClass('ui-selected');
58 | mousedown = false;
59 | },
60 | //a special case is the first run,
61 | //$(".ui-selected, .ui-selecting").length is considered to be 2
62 | selecting : function(event, ui) {
63 | if (($(".ui-selected, .ui-selecting").length > 1 ) && firstRun === false) {
64 | $(event.target).children('.ui-selecting').not(':first').removeClass('ui-selecting');
65 | $(ui.selecting).removeClass("ui-selecting");
66 | } else {
67 | setProjection(ui.selecting.id);
68 | last_selectedItem_id = ui.selecting.id;
69 | if (firstRun) {
70 | firstRun = false;
71 | }
72 | }
73 | }
74 | });
75 | });
76 |
77 | function mouseEventHandler(e) {
78 | "use strict";
79 |
80 | var mouseMove, mouseUp, prevMouse;
81 |
82 | prevMouse = {
83 | x : e.clientX,
84 | y : e.clientY
85 | };
86 |
87 | mouseMove = function(e) {
88 | var unitsPerPixel, lon0, dx = e.clientX - prevMouse.x;
89 |
90 | unitsPerPixel = 1 / map.getScale();
91 | lon0 = map.getCentralLongitude();
92 | lon0 -= dx * unitsPerPixel;
93 | map.setCentralLongitude(lon0);
94 |
95 | prevMouse.x = e.clientX;
96 | prevMouse.y = e.clientY;
97 | e.preventDefault();
98 | };
99 |
100 | mouseUp = function(e) {
101 | document.body.style.cursor = null;
102 | document.removeEventListener('mousemove', mouseMove, false);
103 | document.removeEventListener('mouseup', mouseUp, false);
104 | };
105 |
106 | document.body.style.cursor = 'move';
107 | document.addEventListener('mousemove', mouseMove, false);
108 | document.addEventListener('mouseup', mouseUp, false);
109 | }
110 |
111 | function initAspectSlider() {
112 | "use strict";
113 | function action(event, ui) {
114 | // fix a bug in jQuery slider
115 | // http://stackoverflow.com/questions/9121160/jquery-ui-slider-value-returned-from-slide-event-on-release-is-different-fro
116 | $("#sliderAspect").slider('value', ui.value);
117 | updateSliderTexts();
118 | updateProjection();
119 | deselectButtons();
120 | }
121 |
122 | // create the slider
123 | $("#sliderAspect").slider({
124 | orientation : "horizontal",
125 | range : "min",
126 | min : 30,
127 | max : 100,
128 | value : 60,
129 | step : 1,
130 | slide : action
131 | });
132 | }
133 |
134 | function getMapScale(map) {
135 | "use strict";
136 |
137 | var K = 0.95/2, aspectRatio = $("#sliderAspect").slider("value") / 100;
138 | /*
139 | if (aspectRatio > map.getCanvas().height / map.getCanvas().width) {
140 | return map.getCanvas().height / (Math.PI) * K;
141 | }
142 | return map.getCanvas().width / Math.PI * K;*/
143 |
144 | if (map.getCanvas().height > map.getCanvas().width) {
145 | return map.getCanvas().width / Math.PI * K;
146 | }
147 | return map.getCanvas().height / Math.PI * K
148 | }
149 |
150 |
151 | $(window).load(function() {
152 | "use strict";
153 | // currently styling works like this:
154 | // - if there's a fillStyle then it will be filled
155 | // - if there's a strokeStyle then it will be stroked
156 | // - points are always 3px rectangles
157 | // - polylines can't be filled
158 |
159 | var layers, graticule;
160 |
161 | function Style(strokeStyle, lineWidth, fillStyle) {
162 | if (strokeStyle !== undefined) {
163 | this.strokeStyle = strokeStyle;
164 | this.lineWidth = lineWidth;
165 | }
166 | if (fillStyle !== undefined) {
167 | this.fillStyle = fillStyle;
168 | }
169 | }
170 |
171 | //create the map
172 | graticule = new Graticule(new Style("#77b", "1"), 15);
173 | layers = [];
174 | layers.push(new Layer("../data/ne_110m_coastline", new Style("#888", "1")));
175 | layers.push(graticule);
176 |
177 | map = createMap(layers, new AspectAdaptiveCylindrical(), $("#mapCanvas")[0], $("#mapCanvas").width(), $("#mapCanvas").height());
178 |
179 | $(window).resize(function() {
180 | map.resize($("#mapCanvas").width(), $("#mapCanvas").height());
181 | map.setScale(getMapScale(map));
182 | });
183 |
184 | $("#map").bind("mousedown", mouseEventHandler);
185 |
186 | initAspectSlider();
187 | updateSliderTexts();
188 | updateProjection();
189 | map.setScale(getMapScale(map));
190 | });
191 |
--------------------------------------------------------------------------------
/AspectAdaptive/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Aspect-Adaptive Cylindrical
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | A Compromise Aspect-Adaptive Cylindrical Projection
24 |
25 | A new compromise cylindrical map projection family that adjusts the distribution of parallels to the aspect ratio of a canvas.
26 |
27 |
28 | Developed by Bernhard Jenny (Oregon State University), Bojan Šavrič (Oregon State University), and Tom Patterson (US National Park Service).
29 |
30 |
31 | Jenny, B., Šavrič, B. and Patterson, T. (—).A compromise aspect-adaptive cylindrical projection for world maps . International Journal of Geographical Information Science (in press).
32 |
33 |
34 |
35 |
36 |
37 |
38 | Length Ratio between Equator and Central Meridian
39 |
40 |
41 |
45 |
46 |
47 |
48 | Plate Carrée
49 |
50 |
51 | Compact Miller
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/AspectAdaptive/projection-selection-list.css:
--------------------------------------------------------------------------------
1 | #foldingWarning {
2 | color:red;
3 | margin-top: 12px;
4 | margin: 6px;
5 | display: none;
6 | }
7 |
8 | #StParallel {
9 | margin-bottom: 30px;
10 | }
11 |
12 | #selectable {
13 | list-style-type: none;
14 | margin: 0;
15 | padding: 0;
16 | }
17 | #feedback {
18 | font-size: 1.4em;
19 | }
20 | #selectable .ui-selecting {
21 | background: #BBBBBB;
22 | }
23 | #selectable .ui-selected {
24 | background: #BBBBBB;
25 | color: white;
26 | }
27 | #selectable li {
28 | margin-top: 3px;
29 | margin-bottom: 3px;
30 | padding: 0.4em;
31 | width: 195px;
32 | height: 18px;
33 | }
--------------------------------------------------------------------------------
/CanvasMap - readme.txt:
--------------------------------------------------------------------------------
1 | CanvasMap
2 | =========
3 |
4 | CanvasMap is a simple HTML framework to experiment with projection equations.
5 | CanvasMap uses JavaScript, HTML5 Canvas, and JQuery.
6 |
7 | Programming by Bernhard Jenny and Bojan Savric, College of Earth, Ocean and
8 | Atmospheric Sciences, Oregon State University, and Mostafa El-Fouly, TU Munich.
9 |
10 | Contact: Bernhard Jenny
11 |
12 | License: GNU General Public License Version 2: http://www.gnu.org/licenses/gpl-2.0.html
13 |
14 | File structure
15 | ==============
16 |
17 | CanvasMap contains the core JavaScript files. Code related to the map and projections is
18 | in src/. CanvasMap.js is created by the build.xml ant script. Include this file in the
19 | HTML file.
20 |
21 | Demos
22 | =====
23 |
24 | All code specific to an application is included in the UI.js files. These files construct
25 | the HTML GUI, provide event handlers and create the map with a projection.
26 |
27 | SimpleMap shows a map with a static projection.
28 | CentralMeridianSlider shows a map and slider to adjust the central meridian.
29 | ProjectionList has a menu to select from various projections.
30 | LambertTransitionSlider illustrates Wagner's transformation applied to the Lambert
31 | azimuthal projection to create a variety of equal-area projections.
32 | MillerTransformation illustrates a transformation of the Mercator projection suggested
33 | by Miller in 1942.
34 | StrebeTransformation illustrates a transformation method invented by D. Strebe.
35 |
36 | Programming Model
37 | =================
38 |
39 | UI.js:
40 | DOM event handlers and construction of the model consisting of a map, one or more layers
41 | and a projection.
42 |
43 | layer.js:
44 | A layer loads a shapefile (points, polylines or polygons), and projects and draws the data.
45 | Polygons are not filled. Shapefiles must use 'geographical' coordinates.
46 |
47 | map.js:
48 | Contains an array of layers, a projection, and scale factor applied before drawing the map.
49 | After the layer, canvas, projection and scale are created, invoke map.load to load the
50 | layer geometry.
51 |
52 | Extending CanvasMap with Custom Projections
53 | ===========================================
54 |
55 | To extend CanvasMap with an additional projection, duplicate an existing
56 | projection file in the projections folder and change the function name and the included
57 | toString() and forward() functions.
58 |
59 | The forward function receives longitude and latitude values in radians. The projected
60 | coordinates have to be written to the xy array. The xy parameter is only for returning the
61 | projected coordinates and does not contain valid data when the function is called.
62 |
63 |
64 | Important: use Apache Ant to concatenate and minify all JavaScript files in the src folder.
65 | The default Ant target creates CanvasMap.js and CanvasMap-min.js. To run Ant,
66 | cd to CanvasMap, then type ant.
67 |
68 | To apply the new projection to the map, change UI.js.
69 | If you use the "ProjectionList" map, add your projection to the getProjection function in
70 | UI.js. Also include your projection in the element in index.html. The value for
71 | the new in the element has to be unique and match the projectionName
72 | parameter of getProjection in UI.js.
--------------------------------------------------------------------------------
/CanvasMap/build.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | /* Build time: ${build.time} */
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/CanvasMap/compiler.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OSUCartography/CanvasMap/66ee766eae2184f309703902dfe576c1fe9a601c/CanvasMap/compiler.jar
--------------------------------------------------------------------------------
/CanvasMap/css/projection.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #eee;
3 | font: 12px sans-serif;
4 | }
5 |
6 | h1 {
7 | font: 18px sans-serif;
8 | }
9 |
10 | #container {
11 | }
12 |
13 | .map {
14 | height:700px;
15 | padding:0px;
16 | border:0px;
17 | background-color: #fff;
18 | margin-right: 310px;
19 | position:relative;
20 | }
21 |
22 | .mapCanvas {
23 | width: 100%;
24 | height: 100%;
25 | position:absolute;
26 | }
27 |
28 | .mapControls {
29 | width: 300px;
30 | float: right;
31 | margin-left: 10px;
32 | }
33 |
34 | .sliderContainer {
35 | padding-top: 5px;
36 | padding-bottom: 30px;
37 | width: 300px;
38 | }
39 |
40 | .slider {
41 | float: left;
42 | width: 200px;
43 | }
44 |
45 | .sliderValueText {
46 | float: right;
47 | width: 80px;
48 | }
49 |
50 | .menu {
51 | width: 300px;
52 | margin-bottom: 10px;
53 | }
--------------------------------------------------------------------------------
/CanvasMap/jquery/css/smoothness/images/animated-overlay.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OSUCartography/CanvasMap/66ee766eae2184f309703902dfe576c1fe9a601c/CanvasMap/jquery/css/smoothness/images/animated-overlay.gif
--------------------------------------------------------------------------------
/CanvasMap/jquery/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OSUCartography/CanvasMap/66ee766eae2184f309703902dfe576c1fe9a601c/CanvasMap/jquery/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png
--------------------------------------------------------------------------------
/CanvasMap/jquery/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OSUCartography/CanvasMap/66ee766eae2184f309703902dfe576c1fe9a601c/CanvasMap/jquery/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png
--------------------------------------------------------------------------------
/CanvasMap/jquery/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OSUCartography/CanvasMap/66ee766eae2184f309703902dfe576c1fe9a601c/CanvasMap/jquery/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png
--------------------------------------------------------------------------------
/CanvasMap/jquery/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OSUCartography/CanvasMap/66ee766eae2184f309703902dfe576c1fe9a601c/CanvasMap/jquery/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png
--------------------------------------------------------------------------------
/CanvasMap/jquery/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OSUCartography/CanvasMap/66ee766eae2184f309703902dfe576c1fe9a601c/CanvasMap/jquery/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png
--------------------------------------------------------------------------------
/CanvasMap/jquery/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OSUCartography/CanvasMap/66ee766eae2184f309703902dfe576c1fe9a601c/CanvasMap/jquery/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png
--------------------------------------------------------------------------------
/CanvasMap/jquery/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OSUCartography/CanvasMap/66ee766eae2184f309703902dfe576c1fe9a601c/CanvasMap/jquery/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png
--------------------------------------------------------------------------------
/CanvasMap/jquery/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OSUCartography/CanvasMap/66ee766eae2184f309703902dfe576c1fe9a601c/CanvasMap/jquery/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png
--------------------------------------------------------------------------------
/CanvasMap/jquery/css/smoothness/images/ui-icons_222222_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OSUCartography/CanvasMap/66ee766eae2184f309703902dfe576c1fe9a601c/CanvasMap/jquery/css/smoothness/images/ui-icons_222222_256x240.png
--------------------------------------------------------------------------------
/CanvasMap/jquery/css/smoothness/images/ui-icons_2e83ff_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OSUCartography/CanvasMap/66ee766eae2184f309703902dfe576c1fe9a601c/CanvasMap/jquery/css/smoothness/images/ui-icons_2e83ff_256x240.png
--------------------------------------------------------------------------------
/CanvasMap/jquery/css/smoothness/images/ui-icons_454545_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OSUCartography/CanvasMap/66ee766eae2184f309703902dfe576c1fe9a601c/CanvasMap/jquery/css/smoothness/images/ui-icons_454545_256x240.png
--------------------------------------------------------------------------------
/CanvasMap/jquery/css/smoothness/images/ui-icons_888888_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OSUCartography/CanvasMap/66ee766eae2184f309703902dfe576c1fe9a601c/CanvasMap/jquery/css/smoothness/images/ui-icons_888888_256x240.png
--------------------------------------------------------------------------------
/CanvasMap/jquery/css/smoothness/images/ui-icons_cd0a0a_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OSUCartography/CanvasMap/66ee766eae2184f309703902dfe576c1fe9a601c/CanvasMap/jquery/css/smoothness/images/ui-icons_cd0a0a_256x240.png
--------------------------------------------------------------------------------
/CanvasMap/jquery/css/smoothness/jquery-ui-1.10.2.custom.min.css:
--------------------------------------------------------------------------------
1 | /*! jQuery UI - v1.10.2 - 2013-04-10
2 | * http://jqueryui.com
3 | * Includes: jquery.ui.core.css, jquery.ui.slider.css
4 | * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana%2CArial%2Csans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=highlight_soft&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=flat&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=glass&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=glass&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=glass&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=glass&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=glass&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=flat&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=flat&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px
5 | * Copyright 2013 jQuery Foundation and other contributors Licensed MIT */.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{min-height:0}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-slider{position:relative;text-align:left}.ui-slider .ui-slider-handle{position:absolute;z-index:2;width:1.2em;height:1.2em;cursor:default}.ui-slider .ui-slider-range{position:absolute;z-index:1;font-size:.7em;display:block;border:0;background-position:0 0}.ui-slider.ui-state-disabled .ui-slider-handle,.ui-slider.ui-state-disabled .ui-slider-range{filter:inherit}.ui-slider-horizontal{height:.8em}.ui-slider-horizontal .ui-slider-handle{top:-.3em;margin-left:-.6em}.ui-slider-horizontal .ui-slider-range{top:0;height:100%}.ui-slider-horizontal .ui-slider-range-min{left:0}.ui-slider-horizontal .ui-slider-range-max{right:0}.ui-slider-vertical{width:.8em;height:100px}.ui-slider-vertical .ui-slider-handle{left:-.3em;margin-left:0;margin-bottom:-.6em}.ui-slider-vertical .ui-slider-range{left:0;width:100%}.ui-slider-vertical .ui-slider-range-min{bottom:0}.ui-slider-vertical .ui-slider-range-max{top:0}.ui-widget{font-family:Verdana,Arial,sans-serif;font-size:1.1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Verdana,Arial,sans-serif;font-size:1em}.ui-widget-content{border:1px solid #aaa;background:#fff url(images/ui-bg_flat_75_ffffff_40x100.png) 50% 50% repeat-x;color:#222}.ui-widget-content a{color:#222}.ui-widget-header{border:1px solid #aaa;background:#ccc url(images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x;color:#222;font-weight:bold}.ui-widget-header a{color:#222}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default{border:1px solid #d3d3d3;background:#e6e6e6 url(images/ui-bg_glass_75_e6e6e6_1x400.png) 50% 50% repeat-x;font-weight:normal;color:#555}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited{color:#555;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus{border:1px solid #999;background:#dadada url(images/ui-bg_glass_75_dadada_1x400.png) 50% 50% repeat-x;font-weight:normal;color:#212121}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited{color:#212121;text-decoration:none}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active{border:1px solid #aaa;background:#fff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x;font-weight:normal;color:#212121}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#212121;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #fcefa1;background:#fbf9ee url(images/ui-bg_glass_55_fbf9ee_1x400.png) 50% 50% repeat-x;color:#363636}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#363636}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #cd0a0a;background:#fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x;color:#cd0a0a}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#cd0a0a}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#cd0a0a}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url(images/ui-icons_222222_256x240.png)}.ui-widget-header .ui-icon{background-image:url(images/ui-icons_222222_256x240.png)}.ui-state-default .ui-icon{background-image:url(images/ui-icons_888888_256x240.png)}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon{background-image:url(images/ui-icons_454545_256x240.png)}.ui-state-active .ui-icon{background-image:url(images/ui-icons_454545_256x240.png)}.ui-state-highlight .ui-icon{background-image:url(images/ui-icons_2e83ff_256x240.png)}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url(images/ui-icons_cd0a0a_256x240.png)}.ui-icon-blank{background-position:16px 16px}.ui-icon-carat-1-n{background-position:0 0}.ui-icon-carat-1-ne{background-position:-16px 0}.ui-icon-carat-1-e{background-position:-32px 0}.ui-icon-carat-1-se{background-position:-48px 0}.ui-icon-carat-1-s{background-position:-64px 0}.ui-icon-carat-1-sw{background-position:-80px 0}.ui-icon-carat-1-w{background-position:-96px 0}.ui-icon-carat-1-nw{background-position:-112px 0}.ui-icon-carat-2-n-s{background-position:-128px 0}.ui-icon-carat-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-64px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-64px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:0 -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:4px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:4px}.ui-widget-overlay{background:#aaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x;opacity:.3;filter:Alpha(Opacity=30)}.ui-widget-shadow{margin:-8px 0 0 -8px;padding:8px;background:#aaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x;opacity:.3;filter:Alpha(Opacity=30);border-radius:8px}
--------------------------------------------------------------------------------
/CanvasMap/jquery/jquery.ui.touch-punch.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | * jQuery UI Touch Punch 0.2.2
3 | *
4 | * Copyright 2011, Dave Furfero
5 | * Dual licensed under the MIT or GPL Version 2 licenses.
6 | *
7 | * Depends:
8 | * jquery.ui.widget.js
9 | * jquery.ui.mouse.js
10 | */
11 | (function(b){b.support.touch="ontouchend" in document;if(!b.support.touch){return;}var c=b.ui.mouse.prototype,e=c._mouseInit,a;function d(g,h){if(g.originalEvent.touches.length>1){return;}g.preventDefault();var i=g.originalEvent.changedTouches[0],f=document.createEvent("MouseEvents");f.initMouseEvent(h,true,true,window,1,i.screenX,i.screenY,i.clientX,i.clientY,false,false,false,false,0,null);g.target.dispatchEvent(f);}c._touchStart=function(g){var f=this;if(a||!f._mouseCapture(g.originalEvent.changedTouches[0])){return;}a=true;f._touchMoved=false;d(g,"mouseover");d(g,"mousemove");d(g,"mousedown");};c._touchMove=function(f){if(!a){return;}this._touchMoved=true;d(f,"mousemove");};c._touchEnd=function(f){if(!a){return;}d(f,"mouseup");d(f,"mouseout");if(!this._touchMoved){d(f,"click");}a=false;};c._mouseInit=function(){var f=this;f.element.bind("touchstart",b.proxy(f,"_touchStart")).bind("touchmove",b.proxy(f,"_touchMove")).bind("touchend",b.proxy(f,"_touchEnd"));e.call(f);};})(jQuery);
--------------------------------------------------------------------------------
/CanvasMap/src/Graticule.js:
--------------------------------------------------------------------------------
1 | /*globals LineDrawer, applyStyle*/
2 |
3 | /**
4 | * Map graticule (lines of constant longitude and latitude)
5 | * @param {Object} style Style for drawing the graticule
6 | * @param {Object} lineDistanceDeg Distance in degrees between neighboring lines of constant longitude or latitude.
7 | * @param {Object} maxPointDistance Maximum distance in degrees for drawing lines. Optional. Specify low value (e.g., 1 deg) if graticule is unusually curvy.
8 | */
9 | function Graticule(style, lineDistanceDeg, maxPointDistance) {
10 | "use strict";
11 |
12 | if (maxPointDistance === undefined) {
13 | maxPointDistance = 5;
14 | }
15 | var nVerticalPoints = 180 / Math.min(lineDistanceDeg, maxPointDistance) + 1,
16 | nHorizontalPoints = 360 / Math.min(lineDistanceDeg, maxPointDistance) + 1;
17 |
18 | function drawMeridian(projection, scale, lineDrawer, lon) {
19 | var i,
20 | lat,
21 | dLat = Math.PI / (nVerticalPoints - 1);
22 | for ( i = 0; i < nVerticalPoints; i += 1) {
23 | lat = -Math.PI / 2 + i * dLat;
24 | lineDrawer.projectDraw(lon, lat);
25 | }
26 | lineDrawer.stroke();
27 | }
28 |
29 | function drawParallel(projection, scale, lineDrawer, lat) {
30 | var i,
31 | lon,
32 | dLon = 2 * Math.PI / (nHorizontalPoints - 1);
33 | for ( i = 0; i < nHorizontalPoints; i += 1) {
34 | lon = -Math.PI + i * dLon;
35 | lineDrawer.projectDraw(lon, lat);
36 | }
37 | lineDrawer.stroke();
38 | }
39 |
40 |
41 | this.render = function(projection, lon0, scale, canvas) {
42 | var i,
43 | lon,
44 | lat,
45 | ctx = canvas.getContext('2d'),
46 | lineDrawer = new LineDrawer(0, scale, projection, ctx);
47 |
48 | ctx.save();
49 | ctx.translate(canvas.width / 2, canvas.height / 2);
50 | applyStyle(style, ctx);
51 |
52 | // meridians
53 | for ( i = 0; i < 360 / lineDistanceDeg; i += 1) {
54 | lon = -Math.PI + i * lineDistanceDeg / 180 * Math.PI - lon0;
55 | while (lon < -Math.PI) {
56 | lon += Math.PI * 2;
57 | }
58 | while (lon > Math.PI) {
59 | lon -= Math.PI * 2;
60 | }
61 | drawMeridian(projection, scale, lineDrawer, lon);
62 | }
63 |
64 | // parallels
65 | for ( i = 1; i < 180 / lineDistanceDeg; i += 1) {
66 | lat = -Math.PI / 2 + i * lineDistanceDeg / 180 * Math.PI;
67 | drawParallel(projection, scale, lineDrawer, lat);
68 | }
69 |
70 | // vertical graticule border
71 | if ( typeof (projection.isPoleInsideGraticule) !== 'function' || projection.isPoleInsideGraticule() === false) {
72 | drawMeridian(projection, scale, lineDrawer, -Math.PI);
73 | drawMeridian(projection, scale, lineDrawer, Math.PI);
74 | }
75 |
76 | // horizontal graticule border
77 | drawParallel(projection, scale, lineDrawer, -Math.PI / 2);
78 | drawParallel(projection, scale, lineDrawer, Math.PI / 2);
79 |
80 | ctx.restore();
81 | };
82 |
83 | this.load = function(projection, scale, map) {
84 | // dummy
85 | };
86 | }
87 |
--------------------------------------------------------------------------------
/CanvasMap/src/Layer.js:
--------------------------------------------------------------------------------
1 | /*global LineDrawer, ShpType, ShpFile, DbfFile, BinaryAjax, applyStyle */
2 |
3 | function Layer(url, style) {"use strict";
4 |
5 | var onShpFail, onShpComplete, onDbfFail, onDbfComplete, pointD = 4, layer = this;
6 |
7 | this.shpFile = null;
8 | this.dbfFile = null;
9 |
10 | function adjustLongitude(lon, lon0) {
11 | lon -= lon0;
12 | while (lon < -Math.PI) {
13 | lon += Math.PI * 2;
14 | }
15 | while (lon > Math.PI) {
16 | lon -= Math.PI * 2;
17 | }
18 | return lon;
19 | }
20 |
21 | function renderPoints(projection, lon0, scale, canvas) {
22 |
23 | var ctx, sc, recordID, shapeRecord, shape, projectionPoint, lon, lat, x, y, dx, dy, fill, stroke;
24 |
25 | projectionPoint = [];
26 | dx = canvas.width / 2;
27 | dy = -canvas.height / 2;
28 | ctx = canvas.getContext('2d');
29 | ctx.save();
30 | fill = typeof style !== "undefined" && style.hasOwnProperty("fillStyle");
31 | stroke = typeof style !== "undefined" && style.hasOwnProperty("strokeStyle");
32 |
33 | if (fill && ( typeof style.fillStyle !== "undefined")) {
34 | ctx.fillStyle = style.fillStyle;
35 | }
36 | if (stroke && ( typeof style.strokeStyle !== "undefined")) {
37 | ctx.strokeStyle = style.strokeStyle;
38 | }
39 | if ( typeof style !== "undefined" && style.hasOwnProperty("lineWidth") && typeof style.lineWidth !== "undefined") {
40 | ctx.lineWidth = style.lineWidth;
41 | }
42 |
43 | for ( recordID = 0; recordID < layer.shpFile.records.length; recordID += 1) {
44 | shapeRecord = layer.shpFile.records[recordID];
45 | shape = shapeRecord.shape;
46 |
47 | lon = adjustLongitude(shape.x, lon0);
48 | lat = shape.y;
49 | projection.forward(lon, lat, projectionPoint);
50 | x = projectionPoint[0] * scale + dx;
51 | y = canvas.height - projectionPoint[1] * scale + dy;
52 |
53 | if (fill && typeof style.fillStyle !== 'undefined') {
54 | ctx.fillRect(x - pointD / 2, y - pointD / 2, pointD, pointD);
55 | }
56 | if (stroke && typeof style.strokeStyle !== 'undefined') {
57 | ctx.strokeRect(x - pointD / 2, y - pointD / 2, pointD, pointD);
58 | }
59 | }
60 | ctx.restore();
61 | }
62 |
63 | function renderPolygons(projection, lon0, scale, canvas) {
64 |
65 | var ctx, sc, shapeRecord, recordID, shape, ringID, ring, ptID, lineDrawer;
66 |
67 | ctx = canvas.getContext('2d');
68 | ctx.save();
69 | ctx.translate(canvas.width / 2, canvas.height / 2);
70 | applyStyle(style, ctx);
71 | lineDrawer = new LineDrawer(lon0, scale, projection, ctx);
72 |
73 | for ( recordID = 0; recordID < layer.shpFile.records.length; recordID += 1) {
74 | shapeRecord = layer.shpFile.records[recordID];
75 | shape = shapeRecord.shape;
76 | for ( ringID = 0; ringID < shape.rings.length; ringID += 1) {
77 | ring = shape.rings[ringID];
78 | for ( ptID = 0; ptID < ring.length; ptID += 1) {
79 | lineDrawer.intersectProjectDraw(ring[ptID].x, ring[ptID].y);
80 | }
81 | lineDrawer.stroke();
82 | }
83 | }
84 | ctx.restore();
85 | }
86 |
87 |
88 | this.render = function(projection, lon0, scale, canvas) {
89 |
90 | if (layer.shpFile === null) {
91 | return;
92 | }
93 |
94 | switch (layer.shpFile.header.shapeType) {
95 | case ShpType.SHAPE_POLYGON:
96 | case ShpType.SHAPE_POLYLINE:
97 | renderPolygons(projection, lon0, scale, canvas);
98 | break;
99 | case ShpType.SHAPE_POINT:
100 | renderPoints(projection, lon0, scale, canvas);
101 | break;
102 | }
103 | };
104 |
105 | function pointsDegreeToRadian(records) {
106 | var i, nRecords, c = Math.PI / 180, shp;
107 | for ( i = 0, nRecords = records.length; i < nRecords; i += 1) {
108 | shp = records[i].shape;
109 | shp.x *= c;
110 | shp.y *= c;
111 | }
112 | }
113 |
114 | function linesDegreeToRadian(records) {
115 | var c = Math.PI / 180, nRecords, nRings, nVertices, i, j, ring, k, lon, lat, xMin, xMax, yMin, yMax, shp;
116 | for ( i = 0, nRecords = records.length; i < nRecords; i += 1) {
117 | shp = records[i].shape;
118 | xMin = Number.MAX_VALUE;
119 | xMax = -Number.MAX_VALUE;
120 | yMin = Number.MAX_VALUE;
121 | yMax = -Number.MAX_VALUE;
122 |
123 | for ( j = 0, nRings = shp.rings.length; j < nRings; j += 1) {
124 | ring = shp.rings[j];
125 | for ( k = 0, nVertices = ring.length; k < nVertices; k += 1) {
126 | lon = ring[k].x * c;
127 |
128 | if (lon > xMax) {
129 | xMax = lon;
130 | }
131 | if (lon < xMin) {
132 | xMin = lon;
133 | }
134 |
135 | if (lon > Math.PI) {
136 | lon = Math.PI;
137 | } else if (lon < -Math.PI) {
138 | lon = -Math.PI;
139 | }
140 |
141 | ring[k].x = lon;
142 |
143 | lat = ring[k].y * c;
144 |
145 | // clamp to +/-PI/2
146 | if (lat > Math.PI / 2) {
147 | lat = Math.PI / 2;
148 | } else if (lat < -Math.PI / 2) {
149 | lat = -Math.PI / 2;
150 | }
151 |
152 | if (lat > yMax) {
153 | yMax = lat;
154 | }
155 | if (lat < yMin) {
156 | yMin = lat;
157 | }
158 | ring[k].y = lat;
159 | }
160 | }
161 | shp.box.xMin = xMin;
162 | shp.box.xMax = xMax;
163 | shp.box.yMin = yMin;
164 | shp.box.yMax = yMax;
165 | }
166 | }
167 |
168 |
169 | this.load = function(map) {
170 |
171 | function onShpFail() {
172 | alert('Failed to load ' + url);
173 | }
174 |
175 | function onDbfFail() {
176 | alert('Failed to load ' + url);
177 | }
178 |
179 | function onShpComplete(oHTTP) {
180 | layer.shpFile = new ShpFile(oHTTP.binaryResponse);
181 |
182 | // convert geometry from degrees to radians
183 | switch (layer.shpFile.header.shapeType) {
184 | case ShpType.SHAPE_POLYGON:
185 | case ShpType.SHAPE_POLYLINE:
186 | linesDegreeToRadian(layer.shpFile.records);
187 | break;
188 | case ShpType.SHAPE_POINT:
189 | pointsDegreeToRadian(layer.shpFile.records);
190 | break;
191 | }
192 |
193 | if (layer.dbfFile !== null) {
194 | map.render();
195 | }
196 | }
197 |
198 | function onDbfComplete(oHTTP) {
199 | layer.dbfFile = new DbfFile(oHTTP.binaryResponse);
200 | if (layer.shpFile !== null) {
201 | map.render();
202 | }
203 | }
204 |
205 | var shpLoader, dbfLoader;
206 | try {
207 | shpLoader = new BinaryAjax(url + '.shp', onShpComplete, onShpFail);
208 | dbfLoader = new BinaryAjax(url + '.dbf', onDbfComplete, onDbfFail);
209 | } catch (e) {
210 | alert(e);
211 | }
212 | };
213 | }
214 |
--------------------------------------------------------------------------------
/CanvasMap/src/LineDrawer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Square distance between a point and a line defined by two other points.
3 | * See http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html
4 | * @param x0 The point not on the line.
5 | * @param y0 The point not on the line.
6 | * @param x1 A point on the line.
7 | * @param y1 A point on the line.
8 | * @param x2 Another point on the line.
9 | * @param y2 Another point on the line.
10 | */
11 | function pointLineDistanceSquare(x0, y0, x1, y1, x2, y2) {"use strict";
12 | var d, x2_x1 = x2 - x1, y2_y1 = y2 - y1;
13 | d = (x2_x1) * (y1 - y0) - (x1 - x0) * (y2_y1);
14 | return d * d / (x2_x1 * x2_x1 + y2_y1 * y2_y1);
15 | }
16 |
17 | function adjustLongitude(lon, lon0) {"use strict";
18 | lon -= lon0;
19 | while (lon < -Math.PI) {
20 | lon += Math.PI * 2;
21 | }
22 | while (lon > Math.PI) {
23 | lon -= Math.PI * 2;
24 | }
25 | return lon;
26 | }
27 |
28 | function SphericalRotation(poleLat) {"use strict";
29 | var sinLatPole, cosLatPole;
30 |
31 | sinLatPole = Math.sin(poleLat);
32 | cosLatPole = Math.cos(poleLat);
33 |
34 | this.getPoleLat = function() {
35 | return poleLat;
36 | };
37 |
38 | this.transform = function(lon, lat, res) {
39 | var sinLon, cosLon, sinLat, cosLat, cosLat_x_cosLon;
40 | sinLon = Math.sin(lon);
41 | cosLon = Math.cos(lon);
42 | sinLat = Math.sin(lat);
43 | cosLat = Math.cos(lat);
44 | /*
45 | // FIXME transverse
46 | res[0] = Math.atan2(cosLat * sinLon, sinLat);
47 | res[1] = Math.asin(-cosLat * cosLon);
48 | */
49 | cosLat_x_cosLon = cosLat * cosLon;
50 | res[0] = Math.atan2(cosLat * sinLon, sinLatPole * cosLat_x_cosLon + cosLatPole * sinLat);
51 | sinLat = sinLatPole * sinLat - cosLatPole * cosLat_x_cosLon;
52 | res[1] = Math.asin(sinLat);
53 | };
54 |
55 | this.transformInv = function(lon, lat, res) {
56 | var sinLon = Math.sin(lon), cosLon = Math.cos(lon), sinLat = Math.sin(lat), cosLat = Math.cos(lat);
57 | var cosLat_x_cosLon = cosLat * cosLon;
58 | res[0] = Math.atan2(cosLat * sinLon, sinLatPole * cosLat_x_cosLon - cosLatPole * sinLat);
59 | res[1] = Math.asin(sinLatPole * sinLat + cosLatPole * cosLat_x_cosLon);
60 | };
61 |
62 | }
63 |
64 | function LineDrawer(lon0, scale, projection, ctx) {"use strict";
65 |
66 | var prevX, prevY, prevLon, prevLat, prevPointOutOfRange, moveTo = true, CURVE_TOL, CURVE_TOL_SQR;
67 |
68 | var sphericalRotation = new SphericalRotation(Math.PI / 4);
69 |
70 | // the tolerance by which a line may deviate from a perfectly smooth line
71 | CURVE_TOL = 0.25 / scale;
72 | CURVE_TOL_SQR = CURVE_TOL * CURVE_TOL;
73 |
74 | function drawCmd(lon, lat, lon0, stackDepth) {
75 | var lonMean, latMean, x, y, dsq, xy = [], xyMean = [];
76 |
77 | if ((stackDepth += 1) > 50) {
78 | return;
79 | }
80 |
81 | lon = adjustLongitude(lon, lon0);
82 |
83 | /*var rot = [];
84 | sphericalRotation.transform(lon, lat, rot);
85 | lon = rot[0];
86 | lat = rot[1];
87 | */
88 | projection.forward(lon, lat, xy);
89 |
90 | if (moveTo) {
91 | if (isFinite(xy[0]) && isFinite(xy[1])) {
92 | ctx.beginPath();
93 | ctx.moveTo(xy[0] * scale, -xy[1] * scale);
94 | moveTo = false;
95 | }
96 | } else {
97 | // compute the orthogonal distance in Cartesian coordinates of the mean point to the line
98 | // between the start and the end point
99 | lonMean = (prevLon + lon) * 0.5;
100 | latMean = (prevLat + lat) * 0.5;
101 | projection.forward(lonMean, latMean, xyMean);
102 | dsq = pointLineDistanceSquare(xyMean[0], xyMean[1], prevX, prevY, xy[0], xy[1]);
103 |
104 | if (isFinite(dsq)) {
105 | // if the distance is too large, add intermediate points
106 | if (dsq > CURVE_TOL_SQR) {
107 | drawCmd(lonMean, latMean, 0, stackDepth);
108 | drawCmd(lon, lat, 0, stackDepth);
109 | }
110 |
111 | ctx.lineTo(xy[0] * scale, -xy[1] * scale);
112 | } else {
113 | ctx.stroke();
114 | moveTo = true;
115 | }
116 | }
117 | prevX = xy[0];
118 | prevY = xy[1];
119 | prevLon = lon;
120 | prevLat = lat;
121 | }
122 |
123 |
124 | this.stroke = function() {
125 | ctx.stroke();
126 | moveTo = true;
127 | };
128 |
129 | /**
130 | * Computes two intersection points for a straight line segment that crosses
131 | * the anti-meridian. Projects and draws the two intersection points and
132 | * the end point.
133 | * @param lonEnd The longitude of the end point of the line segment.
134 | * @param latEnd The latitude of the end point of the line segment.
135 | * @param lonStart The longitude of the start point of the line segment.
136 | * @param latStart The latitude of the start point of the line segment.
137 | */
138 | this.projectIntersectingLineTo = function(lonEnd, latEnd, lonStart, latStart) {
139 |
140 | // lon1 the longitude of the intermediate end point
141 | // lon2 the longitude of the intermediate start point
142 | // lat the latitude of both intermediate points
143 | var dLon, dLat, lonMax, lonMin, lon1, lon2, lat;
144 | dLon = lonEnd - lonStart;
145 | dLat = latEnd - latStart;
146 |
147 | // compute intersection point in geographic coordinates
148 | lonMax = Math.PI + lon0;
149 | lonMin = -Math.PI + lon0;
150 |
151 | if (lonEnd > lonMax) {// leaving graticule towards east
152 | lon1 = Math.PI;
153 | lat = latStart + dLat * (lonMax - lonStart) / dLon;
154 | lon2 = -Math.PI;
155 | } else if (lonStart > lonMax) {// entering graticule from east
156 | lon1 = -Math.PI;
157 | lat = latStart + dLat * (lonMax - lonStart) / dLon;
158 | lon2 = Math.PI;
159 | } else if (lonEnd < lonMin) {// leaving graticule towards west
160 | lon1 = -Math.PI;
161 | lat = latStart + dLat * (lonMin - lonStart) / dLon;
162 | lon2 = Math.PI;
163 | } else if (lonStart < lonMin) {// entering graticule from west
164 | lon1 = Math.PI;
165 | lat = latStart + dLat * (lonMin - lonStart) / dLon;
166 | lon2 = -Math.PI;
167 | }
168 | drawCmd(lon1, lat, 0, 1);
169 | this.stroke();
170 | drawCmd(lon2, lat, 0, 1);
171 | drawCmd(lonEnd, latEnd, lon0, 1);
172 | };
173 |
174 | this.projectDraw = function(lon, lat) {
175 | drawCmd(lon, lat, 0, 1);
176 | };
177 |
178 | this.intersectProjectDraw = function(lon, lat) {
179 | var pointOutOfRange;
180 |
181 | if (moveTo) {
182 | prevPointOutOfRange = pointOutOfRange = lon - lon0 < -Math.PI || lon - lon0 > Math.PI;
183 | } else {
184 | pointOutOfRange = lon - lon0 < -Math.PI || lon - lon0 > Math.PI;
185 | }
186 |
187 | if (prevPointOutOfRange !== pointOutOfRange) {
188 | prevPointOutOfRange = pointOutOfRange;
189 | this.projectIntersectingLineTo(lon, lat, prevLon, prevLat);
190 | } else {
191 | drawCmd(lon, lat, lon0, 1);
192 | }
193 | };
194 | }
--------------------------------------------------------------------------------
/CanvasMap/src/Map.js:
--------------------------------------------------------------------------------
1 | // http://www.html5rocks.com/en/tutorials/canvas/hidpi/
2 | function backingScale(ctx) {"use strict";
3 | var devicePixelRatio, backingStoreRatio, ratio, context;
4 | devicePixelRatio = window.devicePixelRatio || 1;
5 | backingStoreRatio = ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1;
6 | ratio = devicePixelRatio / backingStoreRatio;
7 | return ratio;
8 | }
9 |
10 | function applyStyle(style, ctx) {"use strict";
11 | var s, value;
12 |
13 | if ( typeof style !== "undefined") {
14 | for (s in style) {
15 | if (style.hasOwnProperty(s)) {
16 | value = style[s];
17 | if (s === 'lineWidth') {
18 | value *= backingScale(ctx);
19 | }
20 | ctx[s] = value;
21 | }
22 | }
23 | }
24 | }
25 |
26 | function Map(layers, canvas) {"use strict";
27 |
28 | var projection, draw = true, lon0 = 0, scale = 1;
29 |
30 | projection = {
31 | // equirectangular projection
32 | forward : function(lon, lat, xy) {
33 | xy[0] = lon;
34 | xy[1] = lat;
35 | }
36 | };
37 |
38 | this.load = function() {
39 | var i;
40 | for ( i = 0; i < layers.length; i += 1) {
41 | layers[i].load(this);
42 | }
43 | };
44 |
45 | this.getCanvas = function() {
46 | return canvas;
47 | };
48 |
49 | this.getProjection = function() {
50 | return projection;
51 | };
52 |
53 | this.setProjection = function(p) {
54 | projection = p;
55 | this.render();
56 | };
57 |
58 | this.getCentralLongitude = function() {
59 | return lon0;
60 | };
61 |
62 | this.setCentralLongitude = function(centralLongitude) {
63 | if (isNaN(centralLongitude)) {
64 | return;
65 | }
66 | lon0 = centralLongitude;
67 | if (lon0 > Math.PI) {
68 | lon0 -= 2 * Math.PI;
69 | } else if (lon0 < -Math.PI) {
70 | lon0 += 2 * Math.PI;
71 | }
72 | this.render();
73 | };
74 |
75 | this.setDraw = function(d) {
76 | draw = d;
77 | this.render();
78 | };
79 |
80 | this.isDrawn = function() {
81 | return draw;
82 | };
83 |
84 | this.clear = function() {
85 | var ctx = canvas.getContext('2d');
86 | ctx.clearRect(0, 0, canvas.width, canvas.height);
87 | };
88 |
89 | this.getScale = function() {
90 | return scale;
91 | };
92 |
93 | this.setScale = function(s) {
94 | scale = s;
95 | this.render();
96 | };
97 |
98 | this.getMaximumHorizontalScale = function() {
99 | var graticuleWidth, xy = [];
100 | if ( typeof projection.getGraticuleWidth === 'function') {
101 | graticuleWidth = projection.getGraticuleWidth();
102 | } else {
103 | // lon = 180, lat = 0
104 | projection.forward(Math.PI, 0, xy);
105 | graticuleWidth = xy[0] * 2;
106 | // lon = 180, lat = 90
107 | projection.forward(Math.PI, Math.PI / 2, xy);
108 | graticuleWidth = Math.max(graticuleWidth, xy[0]);
109 | }
110 | return canvas.width / graticuleWidth;
111 | };
112 |
113 | this.getMaximumVerticalScale = function() {
114 | var graticuleHeight, xy = [];
115 | if ( typeof projection.getGraticuleHeight === 'function') {
116 | graticuleHeight = projection.getGraticuleHeight();
117 | } else {
118 | // lon = 0, lat = 90
119 | projection.forward(0, Math.PI / 2, xy);
120 | graticuleHeight = xy[1] * 2;
121 | // lon = 180, lat = 90
122 | projection.forward(Math.PI, Math.PI / 2, xy);
123 | graticuleHeight = Math.max(graticuleHeight, xy[1]);
124 |
125 | }
126 | return canvas.height / graticuleHeight;
127 | };
128 |
129 | // FIXME this may return the length of the equator and not the actual graticule width
130 | this.getGraticuleWidth = function() {
131 | var graticuleWidth, xy = [];
132 | if ( typeof projection.getGraticuleWidth === 'function') {
133 | graticuleWidth = projection.getGraticuleWidth();
134 | } else {
135 | // lon = 180, lat = 0
136 | projection.forward(Math.PI, 0, xy);
137 | graticuleWidth = xy[0] * 2;
138 | // lon = 180, lat = 90
139 | projection.forward(Math.PI, Math.PI / 2, xy);
140 | graticuleWidth = Math.max(graticuleWidth, xy[0]);
141 | }
142 | return graticuleWidth;
143 | };
144 |
145 | // FIXME this may return the length of the central meridian and not the actual graticule height
146 | this.getGraticuleHeight = function() {
147 | var graticuleHeight, xy = [];
148 | if ( typeof projection.getGraticuleHeight === 'function') {
149 | graticuleHeight = projection.getGraticuleHeight();
150 | } else {
151 | // lon = 0, lat = 90
152 | projection.forward(0, Math.PI / 2, xy);
153 | graticuleHeight = xy[1] * 2;
154 | // lon = 180, lat = 90
155 | projection.forward(Math.PI, Math.PI / 2, xy);
156 | graticuleHeight = Math.max(graticuleHeight, xy[1]);
157 |
158 | }
159 | return graticuleHeight;
160 | };
161 |
162 | this.scaleMapToCanvas = function() {
163 | var hScale, vScale;
164 | hScale = this.getMaximumHorizontalScale();
165 | vScale = this.getMaximumVerticalScale();
166 | this.setScale(Math.min(hScale, vScale));
167 | };
168 |
169 | this.render = function() {
170 | var i;
171 | this.clear();
172 | if (draw) {
173 | for ( i = 0; i < layers.length; i += 1) {
174 | layers[i].render(projection, lon0, scale, canvas);
175 | }
176 | }
177 | };
178 |
179 | this.resize = function(width, height) {
180 | // use size of DOM element for canvas backing store, and
181 | // increase size of canvas backing store on high-resolution displays, such as Apple's Retina
182 | var scale = backingScale(canvas.getContext('2d'));
183 | canvas.setAttribute('width', width * scale);
184 | canvas.setAttribute('height', height * scale);
185 | this.scaleMapToCanvas();
186 | this.render();
187 | };
188 | }
189 |
190 | function createMap(layers, projection, canvasElement, width, height) {"use strict";
191 | var map = new Map(layers, canvasElement);
192 | map.setProjection(projection);
193 | map.resize(width, height);
194 | map.load();
195 | return map;
196 | }
197 |
--------------------------------------------------------------------------------
/CanvasMap/src/binarywrapper.js:
--------------------------------------------------------------------------------
1 | //
2 | // stateful helper for binaryajax.js's BinaryFile class
3 | //
4 | // modelled on Flash's ByteArray, mostly, although some names
5 | // (int/short/long) differ in definition
6 | //
7 |
8 | function BinaryFileWrapper(binFile) {
9 |
10 | this.position = 0;
11 | this.bigEndian = true;
12 |
13 | this.getByte = function() {
14 | var Byte = binFile.getByteAt(this.position);
15 | this.position++;
16 | return Byte;
17 | }
18 |
19 | this.getLength = function() {
20 | return binFile.getLength();
21 | }
22 |
23 | this.getSByte = function() {
24 | var sbyte = binFile.getSByteAt(this.position);
25 | this.position++;
26 | return sbyte;
27 | }
28 |
29 | this.getShort = function() {
30 | var Short = binFile.getShortAt(this.position, this.bigEndian);
31 | this.position += 2;
32 | return Short;
33 | }
34 |
35 | this.getSShort = function() {
36 | var sshort = binFile.getSShortAt(this.position, this.bigEndian);
37 | this.position += 2;
38 | return sshort;
39 | }
40 |
41 | this.getLong = function() {
42 | var l = binFile.getLongAt(this.position, this.bigEndian);
43 | this.position += 4;
44 | return l;
45 | }
46 |
47 | this.getSLong = function() {
48 | var l = binFile.getSLongAt(this.position, this.bigEndian);
49 | this.position += 4;
50 | return l;
51 | }
52 |
53 | this.getString = function(iLength) {
54 | var s = binFile.getStringAt(this.position, iLength);
55 | this.position += iLength;
56 | return s;
57 | }
58 |
59 | this.getDoubleAt = function(iOffset, bBigEndian) {
60 | // hugs stackoverflow
61 | // http://stackoverflow.com/questions/1597709/convert-a-string-with-a-hex-representation-of-an-ieee-754-double-into-javascript
62 | // TODO: check the endianness for something other than shapefiles
63 | // TODO: what about NaNs and Infinity?
64 | var a = binFile.getLongAt(iOffset + (bBigEndian ? 0 : 4), bBigEndian);
65 | var b = binFile.getLongAt(iOffset + (bBigEndian ? 4 : 0), bBigEndian);
66 | var s = a >> 31 ? -1 : 1;
67 | var e = (a >> 52 - 32 & 0x7ff) - 1023;
68 | return s * (a & 0xfffff | 0x100000) * 1.0 / Math.pow(2,52-32) * Math.pow(2, e) + b * 1.0 / Math.pow(2, 52) * Math.pow(2, e);
69 | }
70 |
71 | this.getDouble = function() {
72 | var d = this.getDoubleAt(this.position, this.bigEndian);
73 | this.position += 8;
74 | return d;
75 | }
76 |
77 | this.getChar = function() {
78 | var c = binFile.getCharAt(this.position);
79 | this.position++;
80 | return c;
81 | }
82 | }
--------------------------------------------------------------------------------
/CanvasMap/src/dbf.js:
--------------------------------------------------------------------------------
1 | // ported from http://code.google.com/p/vanrijkom-flashlibs/ under LGPL v2.1
2 |
3 | function DbfFile(binFile) {
4 |
5 | this.src = new BinaryFileWrapper(binFile);
6 |
7 | var t1 = new Date().getTime();
8 |
9 | this.header = new DbfHeader(this.src);
10 |
11 | var t2 = new Date().getTime();
12 | //if (window.console && window.console.log) console.log('parsed dbf header in ' + (t2-t1) + ' ms');
13 |
14 | t1 = new Date().getTime();
15 |
16 | // TODO: could maybe be smarter about this and only parse these on demand
17 | this.records = [];
18 | for (var i = 0; i < this.header.recordCount; i++) {
19 | var record = this.getRecord(i);
20 | this.records.push(record);
21 | }
22 |
23 | t2 = new Date().getTime();
24 | //if (window.console && window.console.log) console.log('parsed dbf records in ' + (t2-t1) + ' ms');
25 |
26 | }
27 | DbfFile.prototype.getRecord = function(index) {
28 |
29 | if (index > this.header.recordCount) {
30 | throw(new DbfError("",DbfError.ERROR_OUTOFBOUNDS));
31 | }
32 |
33 | this.src.position = this.header.recordsOffset + index * this.header.recordSize;
34 | this.src.bigEndian = false;
35 |
36 | return new DbfRecord(this.src, this.header);
37 | }
38 |
39 |
40 | function DbfHeader(src) {
41 |
42 | // endian:
43 | src.bigEndian = false;
44 |
45 | this.version = src.getSByte();
46 | this.updateYear = 1900+src.getByte();
47 | this.updateMonth = src.getByte();
48 | this.updateDay = src.getByte();
49 | this.recordCount = src.getLong();
50 | this.headerSize = src.getShort();
51 | this.recordSize = src.getShort();
52 |
53 | //skip 2:
54 | src.position += 2;
55 |
56 | this.incompleteTransaction = src.getByte();
57 | this.encrypted = src.getByte();
58 |
59 | // skip 12:
60 | src.position += 12;
61 |
62 | this.mdx = src.getByte();
63 | this.language = src.getByte();
64 |
65 | // skip 2;
66 | src.position += 2;
67 |
68 | // iterate field descriptors:
69 | this.fields = [];
70 | while (src.getSByte() != 0x0D){
71 | src.position -= 1;
72 | this.fields.push(new DbfField(src));
73 | }
74 |
75 | this.recordsOffset = this.headerSize+1;
76 |
77 | }
78 |
79 | function DbfField(src) {
80 |
81 | this.name = this.readZeroTermANSIString(src);
82 |
83 | // fixed length: 10, so:
84 | src.position += (10-this.name.length);
85 |
86 | this.type = src.getByte();
87 | this.address = src.getLong();
88 | this.length = src.getByte();
89 | this.decimals = src.getByte();
90 |
91 | // skip 2:
92 | src.position += 2;
93 |
94 | this.id = src.getByte();
95 |
96 | // skip 2:
97 | src.position += 2;
98 |
99 | this.setFlag = src.getByte();
100 |
101 | // skip 7:
102 | src.position += 7;
103 |
104 | this.indexFlag = src.getByte();
105 | }
106 | DbfField.prototype.readZeroTermANSIString = function(src) {
107 | var r = [];
108 | var b;
109 | while (b = src.getByte()) {
110 | r[r.length] = String.fromCharCode(b);
111 | }
112 | return r.join('');
113 | }
114 |
115 | function DbfRecord(src, header) {
116 | this.offset = src.position;
117 | this.values = {}
118 | for (var i = 0; i < header.fields.length; i++) {
119 | var field = header.fields[i];
120 | this.values[field.name] = src.getString(field.length);
121 | }
122 | }
--------------------------------------------------------------------------------
/CanvasMap/src/lib/binaryajax.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OSUCartography/CanvasMap/66ee766eae2184f309703902dfe576c1fe9a601c/CanvasMap/src/lib/binaryajax.js
--------------------------------------------------------------------------------
/CanvasMap/src/projections/AlbersConic.js:
--------------------------------------------------------------------------------
1 | function AlbersConic() {"use strict";
2 |
3 | var c, rho0, n, n2, EPS10 = 1.0e-10;
4 |
5 | this.toString = function() {
6 | return 'Albers Conic';
7 | };
8 |
9 | this.initialize = function(phi0, phi1, phi2) {
10 | var cosPhi1, sinPhi1, secant;
11 |
12 | if (Math.abs(phi1 + phi2) < EPS10) {
13 | n = NaN;
14 | throw new Error("Standard latitudes of Albers conic too close to equator");
15 | }
16 |
17 | cosPhi1 = Math.cos(phi1);
18 | sinPhi1 = Math.sin(phi1);
19 | secant = Math.abs(phi1 - phi2) >= EPS10;
20 | if (secant) {
21 | n = 0.5 * (sinPhi1 + Math.sin(phi2));
22 | } else {
23 | n = sinPhi1;
24 | }
25 | n2 = 2 * n;
26 | c = cosPhi1 * cosPhi1 + n2 * sinPhi1;
27 | rho0 = Math.sqrt(c - n2 * Math.sin(phi0)) / n;
28 | };
29 |
30 | this.forward = function(lon, lat, xy) {
31 | var rho, n_x_lon;
32 | rho = c - n2 * Math.sin(lat);
33 | if (rho < 0) {
34 | xy[0] = NaN;
35 | xy[1] = NaN;
36 | }
37 | rho = Math.sqrt(rho) / n;
38 | n_x_lon = n * lon;
39 | xy[0] = rho * Math.sin(n_x_lon);
40 | xy[1] = rho0 - rho * Math.cos(n_x_lon);
41 | };
42 |
43 | this.initialize(45/180*Math.PI, 45/180*Math.PI, 45/180*Math.PI);
44 | }
45 |
--------------------------------------------------------------------------------
/CanvasMap/src/projections/Arden-Close.js:
--------------------------------------------------------------------------------
1 | function ArdenClose() {"use strict";
2 |
3 | var MAX_LAT = 88 / 180 * Math.PI;
4 |
5 | this.toString = function() {
6 | return 'Arden-Close';
7 | };
8 |
9 | this.forward = function(lon, lat, xy) {
10 | var y1, y2;
11 |
12 | if (lat > MAX_LAT) {
13 | lat = MAX_LAT;
14 | } else if (lat < -MAX_LAT) {
15 | lat = -MAX_LAT;
16 | }
17 |
18 | y1 = Math.log(Math.tan(Math.PI / 4 + 0.5 * lat));
19 | y2 = Math.sin(lat);
20 |
21 | xy[0] = lon;
22 | xy[1] = (y1 + y2) / 2;
23 | };
24 | }
25 |
--------------------------------------------------------------------------------
/CanvasMap/src/projections/AspectAdaptiveCylindrical.js:
--------------------------------------------------------------------------------
1 | function AspectAdaptiveCylindrical() {"use strict";
2 | var A = [9.684, -33.44, 43.13, -19.77, -0.569, -0.875, 7.002, -5.948, -0.509, 3.333, -6.705, 4.148];
3 | var B = [0.0186, -0.0215, -1.179, 1.837];
4 |
5 | var MIN_ASPECT = 0.3, MAX_ASPECT = 1, PI_HALF = Math.PI / 2, EXTRA_ASPECT_LIMIT = 0.7, EXTRA_LAT_LIMIT = 45 / 180 * Math.PI;
6 | var aspectRatio, k11, k12, k13, k21, k22;
7 |
8 | this.toString = function() {
9 | return 'Aspect Adaptive Cylindrical';
10 | };
11 |
12 | this.getMinAspectRatio = function() {
13 | return MIN_ASPECT;
14 | };
15 |
16 | this.getMaxAspectRatio = function() {
17 | return MAX_ASPECT;
18 | };
19 |
20 | this.getAspectRatio = function(aspect) {
21 | return aspectRatio;
22 | };
23 |
24 | this.setAspectRatio = function(aspect) {
25 | aspectRatio = Math.max(Math.min(aspect, MAX_ASPECT), MIN_ASPECT);
26 |
27 | var k11x, k12x, k13x, n;
28 |
29 | if (aspectRatio > EXTRA_ASPECT_LIMIT) {
30 | k11x = A[0] + EXTRA_ASPECT_LIMIT * (A[1] + EXTRA_ASPECT_LIMIT * (A[2] + EXTRA_ASPECT_LIMIT * A[3]));
31 | k12x = A[4] + EXTRA_ASPECT_LIMIT * (A[5] + EXTRA_ASPECT_LIMIT * (A[6] + EXTRA_ASPECT_LIMIT * A[7]));
32 | k13x = A[8] + EXTRA_ASPECT_LIMIT * (A[9] + EXTRA_ASPECT_LIMIT * (A[10] + EXTRA_ASPECT_LIMIT * A[11]));
33 |
34 | n = PI_HALF * (k11x + PI_HALF * PI_HALF * (k12x + PI_HALF * PI_HALF * k13x));
35 |
36 | k11 = k11x * EXTRA_ASPECT_LIMIT * Math.PI / n;
37 | k12 = k12x * EXTRA_ASPECT_LIMIT * Math.PI / n;
38 | k13 = k13x * EXTRA_ASPECT_LIMIT * Math.PI / n;
39 |
40 | var k21x = B[0] + aspectRatio * B[1];
41 | var k22x = B[2] + aspectRatio * B[3];
42 |
43 | var poleDiff = PI_HALF - EXTRA_ASPECT_LIMIT;
44 | var n_2 = poleDiff * (k21x + poleDiff * poleDiff * k22x );
45 |
46 | var aspectDiff = aspectRatio - EXTRA_ASPECT_LIMIT;
47 | k21 = k21x * Math.PI * aspectDiff / n_2;
48 | k22 = k22x * Math.PI * aspectDiff / n_2;
49 |
50 | } else {
51 | k11x = A[0] + aspectRatio * (A[1] + aspectRatio * (A[2] + aspectRatio * A[3]));
52 | k12x = A[4] + aspectRatio * (A[5] + aspectRatio * (A[6] + aspectRatio * A[7]));
53 | k13x = A[8] + aspectRatio * (A[9] + aspectRatio * (A[10] + aspectRatio * A[11]));
54 |
55 | n = PI_HALF * (k11x + PI_HALF * PI_HALF * (k12x + PI_HALF * PI_HALF * k13x));
56 |
57 | k11 = k11x * aspectRatio * Math.PI / n;
58 | k12 = k12x * aspectRatio * Math.PI / n;
59 | k13 = k13x * aspectRatio * Math.PI / n;
60 |
61 | k21 = k22 = 0.0;
62 | }
63 |
64 | };
65 |
66 | this.forward = function(lon, lat, xy) {
67 | var lat_diff, lat2 = lat * lat;
68 | xy[0] = lon;
69 | xy[1] = lat * (k11 + lat2 * (k12 + lat2 * k13));
70 |
71 | if ((aspectRatio > EXTRA_ASPECT_LIMIT) && (lat > EXTRA_LAT_LIMIT)) {
72 | lat_diff = lat - EXTRA_LAT_LIMIT;
73 | xy[1] += lat_diff * (k21 + lat_diff * lat_diff * k22);
74 | }
75 | if ((aspectRatio > EXTRA_ASPECT_LIMIT) && (lat < -EXTRA_LAT_LIMIT)) {
76 | lat_diff = lat + EXTRA_LAT_LIMIT;
77 | xy[1] += lat_diff * (k21 + lat_diff * lat_diff * k22);
78 | }
79 | };
80 | }
81 |
--------------------------------------------------------------------------------
/CanvasMap/src/projections/Bonne.js:
--------------------------------------------------------------------------------
1 | function Bonne() {"use strict";
2 | var phi1 = 85 / 180 * Math.PI, cotphi1 = 1 / Math.tan(phi1);
3 |
4 | this.toString = function() {
5 | return 'Bonne (Equal Area)';
6 | };
7 |
8 | this.getGraticuleHeight = function() {
9 | return 6;
10 | };
11 |
12 | this.getStandardParallel = function() {
13 | return phi1;
14 | };
15 |
16 | this.setStandardParallel = function(stdParallel) {
17 | phi1 = stdParallel;
18 | cotphi1 = 1 / Math.tan(phi1);
19 | };
20 |
21 | this.forward = function(lon, lat, xy) {
22 | var r = cotphi1 + phi1 - lat;
23 | var E = lon * Math.cos(lat) / r;
24 | xy[0] = r * Math.sin(E);
25 | xy[1] = cotphi1 - r * Math.cos(E);
26 | };
27 | }
28 |
--------------------------------------------------------------------------------
/CanvasMap/src/projections/Braun2.js:
--------------------------------------------------------------------------------
1 | function Braun2() {"use strict";
2 |
3 | this.toString = function() {
4 | return 'Braun2';
5 | };
6 |
7 | this.forward = function(lon, lat, xy) {
8 | xy[0] = lon;
9 | xy[1] = 7 / 5 * Math.sin(lat) / (2 / 5 + Math.cos(lat));
10 | };
11 | }
12 |
--------------------------------------------------------------------------------
/CanvasMap/src/projections/Canters1.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Canters, F. (2002) Small-scale Map projection Design. p. 218-219.
3 | * Modified Sinusoidal, equal-area.
4 | */
5 | function Canters1() {"use strict";
6 |
7 | var C1 = 1.1966, C3 = -0.1290, C3x3 = 3 * C3, C5 = -0.0076, C5x5 = 5 * C5;
8 |
9 | this.toString = function() {
10 | return 'Canters Modified Sinusoidal I';
11 | };
12 |
13 | this.forward = function(lon, lat, xy) {
14 | var y2 = lat * lat,
15 | y4 = y2 * y2;
16 | xy[0] = lon * Math.cos(lat) / (C1 + C3x3 * y2 + C5x5 * y4);
17 | xy[1] = lat * (C1 + C3 * y2 + C5 * y4);
18 | };
19 | }
--------------------------------------------------------------------------------
/CanvasMap/src/projections/Canters2.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Canters, F. (2002) Small-scale Map projection Design. p. 218-220.
3 | * Modified Sinusoidal, equal-area.
4 | */
5 | function Canters2() {"use strict";
6 |
7 | var C1 = 1.1481, C3 = -0.0753, C3x3 = 3 * C3, C5 = -0.0150, C5x5 = 5 * C5;
8 |
9 | this.toString = function() {
10 | return 'Canters Modified Sinusoidal II';
11 | };
12 |
13 | this.forward = function(lon, lat, xy) {
14 | var y2 = lat * lat,
15 | y4 = y2 * y2;
16 | xy[0] = lon * Math.cos(lat) / (C1 + C3x3 * y2 + C5x5 * y4);
17 | xy[1] = lat * (C1 + C3 * y2 + C5 * y4);
18 | };
19 | }
--------------------------------------------------------------------------------
/CanvasMap/src/projections/CentralCylindrical.js:
--------------------------------------------------------------------------------
1 | function CentralCylindrical() {"use strict";
2 |
3 | var MAX_LAT = 80 / 180 * Math.PI;
4 |
5 | this.toString = function() {
6 | return 'Central Cylindrical';
7 | };
8 |
9 | this.forward = function(lon, lat, xy) {
10 | if (lat > MAX_LAT) {
11 | lat = MAX_LAT;
12 | } else if (lat < -MAX_LAT) {
13 | lat = -MAX_LAT;
14 | }
15 | xy[0] = lon;
16 | xy[1] = Math.tan(lat);
17 | };
18 | }
--------------------------------------------------------------------------------
/CanvasMap/src/projections/CylindricalStereographic.js:
--------------------------------------------------------------------------------
1 | function CylindricalStereographic() {"use strict";
2 | var cosPhi0 = Math.cos(30 / 180 * Math.PI);
3 |
4 | this.toString = function() {
5 | return 'Cylindrical Stereographic';
6 | };
7 |
8 | this.getStandardParallel = function() {
9 | return Math.acos(cosPhi0);
10 | };
11 |
12 | this.setStandardParallel = function(phi0) {
13 | cosPhi0 = Math.cos(phi0);
14 | };
15 |
16 | this.forward = function(lon, lat, xy) {
17 | xy[0] = lon * cosPhi0;
18 | xy[1] = (1 + cosPhi0) * Math.tan(lat / 2);
19 | };
20 | }
--------------------------------------------------------------------------------
/CanvasMap/src/projections/Eckert4.js:
--------------------------------------------------------------------------------
1 | function Eckert4() {"use strict";
2 |
3 | var C_x = 0.42223820031577120149,
4 | C_y = 1.32650042817700232218,
5 | C_p = 3.57079632679489661922,
6 | EPS = 1.0e-7,
7 | NITER = 6,
8 | ONE_TOL = 1.00000000000001,
9 | HALFPI = Math.PI / 2;
10 |
11 | this.toString = function() {
12 | return 'Eckert IV';
13 | };
14 |
15 | this.forward = function(lon, lat, xy) {
16 |
17 | var p, V, s, c, i;
18 |
19 | p = C_p * Math.sin(lat);
20 | V = lat * lat;
21 | lat *= 0.895168 + V * (0.0218849 + V * 0.00826809);
22 | for ( i = NITER; i > 0; --i) {
23 | c = Math.cos(lat);
24 | s = Math.sin(lat);
25 | lat -= V = (lat + s * (c + 2) - p) / (1 + c * (c + 2) - s * s);
26 | if (Math.abs(V) < EPS) {
27 | xy[0] = C_x * lon * (1 + Math.cos(lat));
28 | xy[1] = C_y * Math.sin(lat);
29 | return;
30 | }
31 | }
32 | xy[0] = C_x * lon;
33 | xy[1] = lat < 0 ? -C_y : C_y;
34 | };
35 |
36 | this.inverse = function (x, y, lonlat) {
37 | var sinTheta = y / 1.3265004;
38 | var theta = Math.asin(sinTheta);
39 | lonlat[0] = x / (0.4222382 * (1 + Math.cos(theta)));
40 | lonlat[1] = Math.asin((theta + sinTheta * Math.cos(theta) + 2 * sinTheta) / (2 + Math.PI / 2));
41 | };
42 |
43 | }
--------------------------------------------------------------------------------
/CanvasMap/src/projections/Eckert6.js:
--------------------------------------------------------------------------------
1 | function Eckert6() {"use strict";
2 |
3 | var n = 2.570796326794896619231321691,
4 | C_y = Math.sqrt((2) / n),
5 | C_x = C_y / 2,
6 | MAX_ITER = 8,
7 | LOOP_TOL = 1e-7;
8 |
9 | this.forward = function(lon, lat, xy) {
10 | var i, v, k = n * Math.sin(lat);
11 | for (i = MAX_ITER; i > 0;) {
12 | lat -= v = (lat + Math.sin(lat) - k) / (1 + Math.cos(lat));
13 | if (Math.abs(v) < LOOP_TOL) {
14 | break;
15 | }
16 | --i;
17 | }
18 |
19 | xy[0] = C_x * lon * (1 + Math.cos(lat));
20 | xy[1] = C_y * lat;
21 | };
22 |
23 | this.inverse = function (x, y, lonlat) {
24 | y /= C_y;
25 | lonlat[1] = Math.asin((y + Math.sin(y)) / n);
26 | lonlat[0] = x / (C_x * (1 + Math.cos(y)));
27 | };
28 |
29 | this.toString = function() {
30 | return 'Eckert VI';
31 | };
32 | }
--------------------------------------------------------------------------------
/CanvasMap/src/projections/EqualAreaCylindrical.js:
--------------------------------------------------------------------------------
1 | function EqualAreaCylindrical() {"use strict";
2 | var cosPhi0 = Math.cos(30 / 180 * Math.PI);
3 |
4 | this.toString = function() {
5 | return 'Cylindrical Equal-Area';
6 | };
7 |
8 | this.getStandardParallel = function() {
9 | return Math.acos(cosPhi0);
10 | };
11 |
12 | this.setStandardParallel = function(phi0) {
13 | cosPhi0 = Math.cos(phi0);
14 | };
15 |
16 | this.forward = function(lon, lat, xy) {
17 | xy[0] = lon * cosPhi0;
18 | xy[1] = Math.sin(lat) / cosPhi0;
19 | };
20 | }
--------------------------------------------------------------------------------
/CanvasMap/src/projections/Equirectangular.js:
--------------------------------------------------------------------------------
1 | function Equirectangular() {"use strict";
2 | var cosPhi0 = Math.cos(30 / 180 * Math.PI);
3 |
4 | this.toString = function() {
5 | return 'Equirectangular';
6 | };
7 |
8 | this.getStandardParallel = function() {
9 | return Math.acos(cosPhi0);
10 | };
11 |
12 | this.setStandardParallel = function(phi0) {
13 | cosPhi0 = Math.cos(phi0);
14 | };
15 |
16 | this.forward = function(lon, lat, xy) {
17 | xy[0] = lon * cosPhi0;
18 | xy[1] = lat;
19 | };
20 |
21 | this.inverse = function (x, y, lonlat) {
22 | lonlat[0] = x / cosPhi0;
23 | lonlat[1] = y;
24 | };
25 | }
26 |
--------------------------------------------------------------------------------
/CanvasMap/src/projections/Hammer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Custom shapes between a Lambert azimuthal (w = 1) and a Quartic Authalic (w = 0) can be defined with
3 | * the optional argument w. If not provided, the default 0.5 for the Hammer projection is used.
4 | */
5 | function Hammer(w) {
6 |
7 | "use strict";
8 |
9 | var w, W_MAX = 0.999999, EPS10 = 1.e-10;
10 |
11 | this.toString = function() {
12 | switch (w) {
13 | case 0:
14 | return 'Quartic Authalic';
15 | case 0.5:
16 | return 'Hammer';
17 | default:
18 | return 'Hammer Customized';
19 | }
20 | };
21 |
22 | this.forward = function(lon, lat, xy) {
23 | var cosLat = Math.cos(lat), d;
24 | lon *= w;
25 | d = Math.sqrt(2 / (1 + cosLat * Math.cos(lon)));
26 | xy[0] = d * cosLat * Math.sin(lon) / w;
27 | xy[1] = d * Math.sin(lat);
28 | };
29 |
30 | this.inverse = function (x, y, lonlat) {
31 | var EPS = 1.0e-10;
32 | var wx = w * x;
33 | var z = Math.sqrt(1 - 0.25 * (wx * wx + y * y));
34 | var zz2_1 = 2 * z * z - 1;
35 | if(Math.abs(zz2_1) < EPS) {
36 | lonlat[0] = NaN;
37 | lonlat[1] = NaN;
38 | } else {
39 | lonlat[0] = Math.atan2(wx * z, zz2_1) / w;
40 | lonlat[1] = Math.asin(z * y);
41 | }
42 | };
43 |
44 | this.setW = function(weight) {
45 | w = weight;
46 | if(w >= W_MAX) {
47 | w = W_MAX;
48 | } else if(w < 0) {
49 | w = 0;
50 | }
51 | if (w === 0) {
52 | this.forward = this.quarticAuthalicForward;
53 | this.inverse = this.quarticAuthalicInverse;
54 | } else {
55 | //it's already set
56 | //this.forward = hammerForward;
57 | //this.inverse = hammerInverse;
58 | }
59 | };
60 | this.setW(arguments.length === 0 ? 0.5 : w);
61 |
62 | this.getW = function() {
63 | return w;
64 | };
65 |
66 | }
--------------------------------------------------------------------------------
/CanvasMap/src/projections/Hufnagel.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The Hufnagel projection family was introduced by Herbert Hufnagel in
3 | * "Hufnagel, H. 1989. Ein System unecht-zylindrischer Kartennetze für
4 | * Erdkarten. Kartographische Nachrichten, 39(3), 89–96." All projections are
5 | * equal-area. Implementation by Bernhard Jenny, Oregon State University, Bojan
6 | * Savric, Oregon State University, with substantial contributions by Daniel
7 | * "daan" Strebe, Mapthematics. November 2014 to October 2015.
8 | *
9 | * @author Bojan Savric
10 | * @author Bernhard Jenny
11 | */
12 |
13 | function Hufnagel() {
14 | "use strict";
15 |
16 | // a, b, psiMax and aspectRatio parameterize the Hufnagel transformation.
17 | var a = 0,
18 | b = 0,
19 | psiMax = Math.PI / 2,
20 | // ratio between equator and central meridian lengths
21 | aspectRatio = 2,
22 | // tolerance for iterative computations
23 | EPS = 1.0e-12,
24 | // maximum number of computations
25 | MAX_ITER = 100,
26 | // size of lookup table
27 | LUT_SIZE = 101,
28 | // lookup tables, yLUT, would be needed for inverse projection
29 | latLUT,
30 | psiLUT,
31 | // parameters pre-computed in init() from a, b, psiMax and aspectRatio
32 | ksq,
33 | k,
34 | c,
35 | // true if the graticule is folding over itself. Only an approximation, not all cases with folding graticules are captured.
36 | graticuleFolding = false;
37 |
38 | function approximatePsiFromLookupTable(lat) {
39 | var w,
40 | psi,
41 | lat_abs = Math.abs(lat),
42 | imid,
43 | imin = 0,
44 | imax =
45 | LUT_SIZE;
46 |
47 | while (true) {
48 | imid = Math.floor((imin + imax) / 2);
49 | if (imid === imin) {
50 | // This also handles abs(phi) == latitudeTable[0] because mid must == min.
51 | break;
52 | } else if (lat_abs > latLUT[imid]) {
53 | imin = imid;
54 | } else {
55 | // abs(phi) < latitudeTable[mid], or abs(phi) == latitudeTable[mid] and mid ≠ 0
56 | imax = imid;
57 | }
58 | }
59 |
60 | w = (lat_abs - latLUT[imin]) / (latLUT[imin + 1] - latLUT[imin]);
61 | psi = w * (psiLUT[imin + 1] - psiLUT[imin]) + psiLUT[imin];
62 |
63 | return lat < 0 ? -psi : psi;
64 | }
65 |
66 | function initializeLookUpTables() {
67 | var i,
68 | psi,
69 | sin2Psi,
70 | sin4Psi,
71 | sin6Psi,
72 | sinPhi,
73 | phi,
74 | r,
75 | y;
76 | latLUT = [];
77 | psiLUT = [];
78 | graticuleFolding = false;
79 | for ( i = 0; i < LUT_SIZE; i = i + 1) {
80 | // psi is linearly proportional to i
81 | psi = psiMax * i / (LUT_SIZE - 1);
82 |
83 | // phi computed from psi: for equation see Hufnagel 1989
84 | if (i === 0) {
85 | phi = 0;
86 | } else if (i === LUT_SIZE - 1) {
87 | phi = Math.PI / 2;
88 | } else {
89 | sin2Psi = Math.sin(2 * psi);
90 | sin4Psi = Math.sin(4 * psi);
91 | sin6Psi = Math.sin(6 * psi);
92 | sinPhi = 0.25 / Math.PI * k * k * (2 * psi + (1 + a - 0.5 * b) * sin2Psi + 0.5 * (a + b) * sin4Psi + 0.5 * b * sin6Psi);
93 | if (Math.abs(sinPhi) > 1) {
94 | phi = sinPhi > 0 ? Math.PI / 2 : -Math.PI / 2;
95 | } else {
96 | phi = Math.asin(sinPhi);
97 | }
98 | }
99 |
100 | // store values in lookup tables
101 | latLUT.push(phi);
102 | psiLUT.push(psi);
103 | }
104 | }
105 |
106 | this.forward = function(lon, lat, xy) {
107 | var r,
108 | deltaPsi,
109 | deltaPsiNumerator,
110 | deltaPsiDenominator,
111 | psi = 0,
112 | i = 0,
113 | PI_x_sinLat = Math.PI * Math.sin(lat),
114 | psi_x_2;
115 |
116 | if (psiMax === 0) {
117 | // cylindrical equal-area projection
118 | xy[0] = lon * k;
119 | xy[1] = Math.sin(lat) / k;
120 | return;
121 | }
122 | // approximate psi from lookup table
123 | psi = approximatePsiFromLookupTable(lat);
124 |
125 | // iteratively improve psi
126 | while (true) {
127 | psi_x_2 = psi * 2;
128 | deltaPsiNumerator = (ksq / 4) * (psi_x_2 + (1 + a - b / 2) * Math.sin(psi_x_2) + ((a + b) / 2) * Math.sin(2 * psi_x_2) + (b / 2) * Math.sin(3 * psi_x_2)) - PI_x_sinLat;
129 | if (Math.abs(deltaPsiNumerator) < EPS) {
130 | break;
131 | }
132 | deltaPsiDenominator = (ksq / 2) * (1 + (1 + a - (b / 2)) * Math.cos(psi_x_2) + (a + b) * Math.cos(2 * psi_x_2) + (3 * b / 2) * Math.cos(3 * psi_x_2));
133 | deltaPsi = deltaPsiNumerator / deltaPsiDenominator;
134 |
135 | i = i + 1;
136 | if (!isFinite(deltaPsi) || i > MAX_ITER) {
137 | xy[0] = NaN;
138 | xy[1] = NaN;
139 | return;
140 | }
141 | psi = psi - deltaPsi;
142 | }
143 |
144 | // calculate x and y
145 | r = Math.sqrt(1 + a * Math.cos(2 * psi) + b * Math.cos(4 * psi));
146 | xy[0] = k * r * c * lon / Math.PI * Math.cos(psi);
147 | xy[1] = k * r / c * Math.sin(psi);
148 | };
149 |
150 | this.init = function() {
151 | if (psiMax === 0) {
152 | k = Math.sqrt(aspectRatio / Math.PI);
153 | } else {
154 | var xy = [],
155 | width;
156 | ksq = (4 * Math.PI) / (2 * psiMax + (1 + a - b / 2) * Math.sin(2 * psiMax) + ((a + b) / 2) * Math.sin(4 * psiMax) + (b / 2) * Math.sin(6 * psiMax));
157 | k = Math.sqrt(ksq);
158 | c = Math.sqrt(aspectRatio * Math.sin(psiMax)
159 | * Math.sqrt((1 + a * Math.cos(2 * psiMax) + b * Math.cos(4 * psiMax)) / (1 + a + b)));
160 | initializeLookUpTables();
161 | }
162 | };
163 |
164 | this.init();
165 |
166 | this.setA = function(newA) {
167 | a = newA;
168 | this.init();
169 | };
170 |
171 | this.setB = function(newB) {
172 | b = newB;
173 | this.init();
174 | };
175 |
176 | /**
177 | * psi max in radians
178 | */
179 | this.setPsiMax = function(newPsiMax) {
180 | psiMax = newPsiMax;
181 | this.init();
182 | };
183 |
184 | /**
185 | * ratio between equator and central meridian, default is 2.0
186 | */
187 | this.setAspectRatio = function(newAspectRatio) {
188 | aspectRatio = newAspectRatio;
189 | this.init();
190 | };
191 |
192 | /**
193 | * Returns true if the graticule is folding, false otherwise.
194 | */
195 | this.isGraticuleFolding = function() {
196 | return graticuleFolding;
197 | };
198 |
199 | this.toString = function() {
200 | return 'Hufnagel';
201 | };
202 |
203 | }
--------------------------------------------------------------------------------
/CanvasMap/src/projections/Kavrayskiy1.js:
--------------------------------------------------------------------------------
1 | function Kavrayskiy1() {"use strict";
2 |
3 | var PI_HALF = Math.PI / 2, MERCATOR_MAX_LAT = 70 / 180 * Math.PI, DY, C;
4 | DY = Math.log(Math.tan(0.5 * (PI_HALF + MERCATOR_MAX_LAT)));
5 | C = 1 / Math.cos(MERCATOR_MAX_LAT);
6 |
7 | this.toString = function() {
8 | return 'Kavrayskiy I';
9 | };
10 |
11 | this.forward = function(lon, lat, xy) {
12 |
13 | xy[0] = lon;
14 | if (lat > MERCATOR_MAX_LAT) {
15 | xy[1] = (lat - MERCATOR_MAX_LAT) * C + DY;
16 | } else if (lat < -MERCATOR_MAX_LAT) {
17 | xy[1] = (lat + MERCATOR_MAX_LAT) * C - DY;
18 | } else {
19 | xy[1] = Math.log(Math.tan(0.5 * (PI_HALF + lat)));
20 | }
21 | };
22 | }
23 |
--------------------------------------------------------------------------------
/CanvasMap/src/projections/Kavrayskiy5.js:
--------------------------------------------------------------------------------
1 | function Kavrayskiy5() {
2 | "use strict";
3 |
4 | var sineSeries = new SineSeries(1.504875, 1.504875 * 0.9);
5 |
6 | this.toString = function () {
7 | return 'Kavrayskiy V';
8 | };
9 |
10 | this.forward = sineSeries.forward;
11 | this.inverse = sineSeries.inverse;
12 | }
--------------------------------------------------------------------------------
/CanvasMap/src/projections/KharchenkoShabanova.js:
--------------------------------------------------------------------------------
1 | function KharchenkoShabanova() {"use strict";
2 | var K = Math.cos(10 / 180 * Math.PI);
3 |
4 | this.toString = function() {
5 | return 'Kharchenko-Shabanova';
6 | };
7 |
8 | this.forward = function(lon, lat, xy) {
9 | var latSqr = lat * lat;
10 |
11 | xy[0] = lon * K;
12 | xy[1] = lat * (0.99 + latSqr * (0.0026263 + latSqr * 0.10734));
13 | };
14 | }
15 |
--------------------------------------------------------------------------------
/CanvasMap/src/projections/LambertAzimuthalEqualAreaPolar.js:
--------------------------------------------------------------------------------
1 | function LambertAzimuthalEqualAreaPolar() {"use strict";
2 |
3 | var FORTPI = Math.PI / 4, southPole = false;
4 |
5 | this.toString = function() {
6 | return 'Lambert Azimuthal Equal Area - Polar';
7 | };
8 |
9 | //forwardNorthPole
10 | this.forward = function(lon, lat, xy) {
11 | var y = 2 * Math.sin(FORTPI - lat * 0.5);
12 | xy[0] = y * Math.sin(lon);
13 | xy[1] = y * -Math.cos(lon);
14 | };
15 |
16 | function forwardSouthPole(lon, lat, xy) {
17 | var y = 2 * Math.cos(FORTPI - lat * 0.5);
18 | xy[0] = y * Math.sin(lon);
19 | xy[1] = y * Math.cos(lon);
20 | }
21 |
22 |
23 | this.getGraticuleWidth = function() {
24 | return 4;
25 | };
26 |
27 | this.getGraticuleHeight = function() {
28 | return 4;
29 | };
30 |
31 | this.isPoleInsideGraticule = function() {
32 | return true;
33 | };
34 | }
--------------------------------------------------------------------------------
/CanvasMap/src/projections/LambertAzimuthalOblique.js:
--------------------------------------------------------------------------------
1 | function LambertAzimuthalOblique() {"use strict";
2 |
3 | var EPS10 = 1.e-10, lat0 = 0, cosLat0 = 1, sinLat0 = 0;
4 |
5 | this.toString = function() {
6 | return 'Lambert Azimuthal Oblique';
7 | };
8 |
9 | this.initialize = function(lat0) {
10 | cosLat0 = Math.cos(lat0);
11 | sinLat0 = Math.sin(lat0);
12 | };
13 |
14 | this.forward = function(lon, lat, xy) {
15 | var y, sinLat = Math.sin(lat), cosLat = Math.cos(lat), cosLon = Math.cos(lon), sinLon = Math.sin(lon);
16 | y = 1 + sinLat0 * sinLat + cosLat0 * cosLat * cosLon;
17 | // the projection is indeterminate for lon = PI and lat = -lat0
18 | // this point would have to be plotted as a circle
19 | // The following Math.sqrt will return NaN in this case.
20 | y = Math.sqrt(2 / y);
21 | xy[0] = y * cosLat * sinLon;
22 | xy[1] = y * (cosLat0 * sinLat - sinLat0 * cosLat * cosLon);
23 | };
24 |
25 | this.initialize(45*Math.PI/180);
26 | }
27 |
--------------------------------------------------------------------------------
/CanvasMap/src/projections/LambertEqualAreaCylindrical.js:
--------------------------------------------------------------------------------
1 | function LambertEqualAreaCylindrical() {"use strict";
2 |
3 | this.toString = function() {
4 | return 'Lambert Cylindrical Equal-Area';
5 | };
6 |
7 | this.forward = function(lon, lat, xy) {
8 | xy[0] = lon;
9 | xy[1] = Math.sin(lat);
10 | };
11 |
12 | this.inverse = function (x, y, lonlat) {
13 | lonlat[0] = x;
14 | lonlat[1] = Math.asin(y);
15 | };
16 | }
17 |
--------------------------------------------------------------------------------
/CanvasMap/src/projections/McBrydeThomas1.js:
--------------------------------------------------------------------------------
1 | function McBrydeThomas1() {
2 | "use strict";
3 |
4 | var sineSeries = new SineSeries(1.48875, 1.36509);
5 |
6 | this.toString = function () {
7 | return 'McBryde-Thomas I';
8 | };
9 |
10 | this.forward = sineSeries.forward;
11 | this.inverse = sineSeries.inverse;
12 | }
--------------------------------------------------------------------------------
/CanvasMap/src/projections/McBrydeThomas2.js:
--------------------------------------------------------------------------------
1 | function McBrydeThomas2() {
2 | "use strict";
3 |
4 | var MAX_ITER = 10,
5 | LOOP_TOL = 1e-7,
6 | C1 = 0.45503,
7 | C2 = 1.36509,
8 | C3 = 1.41546,
9 | C_x = 0.22248,
10 | C_y = 1.44492,
11 | C1_2 = 0.33333333333333333333333333;
12 |
13 |
14 | this.toString = function () {
15 | return 'McBryde-Thomas II';
16 | };
17 |
18 | this.forward = function (lon, lat, xy) {
19 | var V, t, i, k = C3 * Math.sin(lat);
20 | for (i = MAX_ITER; i; --i) {
21 | t = lat / C2;
22 | lat -= V = (C1 * Math.sin(t) + Math.sin(lat) - k) /
23 | (C1_2 * Math.cos(t) + Math.cos(lat));
24 | if (Math.abs(V) < LOOP_TOL)
25 | break;
26 | }
27 | t = lat / C2;
28 | xy[0] = C_x * lon * (1. + 3. * Math.cos(lat) / Math.cos(t));
29 | xy[1] = C_y * Math.sin(t);
30 | };
31 |
32 | this.inverse = function (x, y, lonlat) {
33 | var t = Math.asin(y / C_y),
34 | phi = C2 * t;
35 | lonlat[0] = x / (C_x * (1 + 3 * Math.cos(phi) / Math.cos(t)));
36 | lonlat[1] = Math.asin((C1 * Math.sin(t) + Math.sin(phi)) / C3);
37 | };
38 | }
--------------------------------------------------------------------------------
/CanvasMap/src/projections/Mercator.js:
--------------------------------------------------------------------------------
1 | function Mercator() {"use strict";
2 |
3 | var PI_HALF = Math.PI / 2, WEB_MERCATOR_MAX_LAT = 1.4844222297453322;
4 |
5 | this.toString = function() {
6 | return 'Mercator';
7 | };
8 |
9 | this.forward = function(lon, lat, xy) {
10 | xy[0] = lon;
11 | if (lat > WEB_MERCATOR_MAX_LAT) {
12 | xy[1] = Math.PI;
13 | } else if (lat < -WEB_MERCATOR_MAX_LAT) {
14 | xy[1] = -Math.PI;
15 | } else {
16 | xy[1] = Math.log(Math.tan(0.5 * (PI_HALF + lat)));
17 | }
18 | };
19 | }
20 |
--------------------------------------------------------------------------------
/CanvasMap/src/projections/Miller.js:
--------------------------------------------------------------------------------
1 | // by O. M. Miller
2 | function Miller() {"use strict";
3 |
4 | this.toString = function() {
5 | return 'Miller';
6 | };
7 |
8 | this.forward = function(lon, lat, xy) {
9 | xy[0] = lon;
10 | xy[1] = Math.log(Math.tan(Math.PI / 4 + lat * 0.4)) * 1.25;
11 | };
12 | }
13 |
--------------------------------------------------------------------------------
/CanvasMap/src/projections/Miller2.js:
--------------------------------------------------------------------------------
1 | // by O. M. Miller
2 | function Miller2() {"use strict";
3 |
4 | this.toString = function() {
5 | return 'Miller II';
6 | };
7 |
8 | this.forward = function(lon, lat, xy) {
9 | xy[0] = lon;
10 | xy[1] = Math.log(Math.tan(Math.PI / 4 + lat / 3)) * 1.5;
11 | };
12 | }
13 |
--------------------------------------------------------------------------------
/CanvasMap/src/projections/MillerPerspective.js:
--------------------------------------------------------------------------------
1 | function MillerPerspective() {"use strict";
2 |
3 | this.toString = function() {
4 | return 'Miller Perspective';
5 | };
6 |
7 | this.forward = function(lon, lat, xy) {
8 | xy[0] = lon;
9 | xy[1] = (Math.sin(lat / 2) + Math.tan(lat / 2));
10 | };
11 | }
12 |
--------------------------------------------------------------------------------
/CanvasMap/src/projections/MillerTransformation.js:
--------------------------------------------------------------------------------
1 | function MillerTransformation() {"use strict";
2 |
3 | var PI_HALF = Math.PI / 2, EPS = 1e-4, n = 1.5, m = 1.5;
4 |
5 | this.toString = function() {
6 | return 'Miller Transformation';
7 | };
8 |
9 | this.getM = function() {
10 | return m;
11 | };
12 |
13 | this.setM = function(M) {
14 | m = M;
15 | };
16 |
17 | this.getN = function() {
18 | return n;
19 | };
20 |
21 | this.setN = function(N) {
22 | n = N;
23 | };
24 |
25 | this.getAspectRatio = function() {
26 | return n * Math.log(Math.tan(0.5 * PI_HALF * (1 + 1 / n))) / Math.PI;
27 | };
28 |
29 | this.setAspectRatio = function(aspect) {
30 | if (aspect <= 0.5) {
31 | m = n = 1 / EPS;
32 | } else {
33 | var Cc = 1.05, tol = 1, F, Fder1, Fder2, angle;
34 | while (Math.abs(tol) > EPS) {
35 | angle = 0.5 * PI_HALF * (1 + 1 / Cc);
36 | F = Cc * Math.log(Math.tan(angle)) - aspect * Math.PI;
37 | Fder1 = Math.log(Math.tan(angle));
38 | Fder2 = Math.tan(angle) * Math.cos(angle) * Math.cos(angle) * Cc;
39 | Cc -= tol = F / (Fder1 - 0.5 * PI_HALF / Fder2);
40 | }
41 | m = n = Cc;
42 | }
43 | };
44 |
45 | this.forward = function(lon, lat, xy) {
46 | xy[0] = lon;
47 | xy[1] = m * Math.log(Math.tan(0.5 * (PI_HALF + lat / n)));
48 | };
49 | }
50 |
--------------------------------------------------------------------------------
/CanvasMap/src/projections/Mollweide.js:
--------------------------------------------------------------------------------
1 | function Mollweide() {"use strict";
2 |
3 | var MAX_ITER = 10,
4 | TOLERANCE = 1.0e-7,
5 | cx, cy, cp;
6 |
7 | this.toString = function() {
8 | return 'Mollweide';
9 | };
10 |
11 | // FIXME
12 | (function() {
13 | var p = Math.PI / 2, r, sp, p2 = p + p;
14 | sp = Math.sin(p);
15 | r = Math.sqrt(Math.PI * 2.0 * sp / (p2 + Math.sin(p2)));
16 | cx = 2 * r / Math.PI;
17 | cy = r / sp;
18 | cp = p2 + Math.sin(p2);
19 | })();
20 |
21 | this.forward = function(lon, lat, xy) {
22 | var k, v, i;
23 | k = cp * Math.sin(lat);
24 | for ( i = MAX_ITER; i !== 0; i--) {
25 | lat -= v = (lat + Math.sin(lat) - k) / (1 + Math.cos(lat));
26 | if (Math.abs(v) < TOLERANCE) {
27 | break;
28 | }
29 | }
30 | if (i === 0) {
31 | lat = (lat < 0) ? -Math.PI / 2 : Math.PI / 2;
32 | } else {
33 | lat *= 0.5;
34 | }
35 | xy[0] = cx * lon * Math.cos(lat);
36 | xy[1] = cy * Math.sin(lat);
37 | };
38 |
39 | this.inverse = function (x, y, lonlat) {
40 | var theta, sinTheta, cosTheta;
41 | // 1 / sqrt(2) = 0.70710678118655
42 | sinTheta = y * 0.70710678118655;
43 | theta = Math.asin(sinTheta);
44 | cosTheta = Math.cos(theta);
45 | // Math.PI / (2 * sqrt(2)) = 1.11072073453959
46 | lonlat[0] = x * 1.11072073453959 / cosTheta;
47 | lonlat[1] = Math.asin(2 * (theta + sinTheta * cosTheta) / Math.PI);
48 | };
49 | }
--------------------------------------------------------------------------------
/CanvasMap/src/projections/NaturalEarth.js:
--------------------------------------------------------------------------------
1 | function NaturalEarth() {"use strict";
2 |
3 | var MAX_Y = 0.8707 * 0.52 * Math.PI;
4 |
5 | this.toString = function() {
6 | return 'Natural Earth';
7 | };
8 |
9 | this.forward = function(lon, lat, xy) {
10 | var lat2 = lat * lat, lat4 = lat2 * lat2;
11 |
12 | xy[0] = lon * (0.8707 - 0.131979 * lat2 + lat4 * (-0.013791 + lat4 * (0.003971 * lat2 - 0.001529 * lat4)));
13 | xy[1] = lat * (1.007226 + lat2 * (0.015085 + lat4 * (-0.044475 + 0.028874 * lat2 - 0.005916 * lat4)));
14 | };
15 | }
--------------------------------------------------------------------------------
/CanvasMap/src/projections/Pavlov.js:
--------------------------------------------------------------------------------
1 | function Pavlov() {"use strict";
2 |
3 | this.toString = function() {
4 | return 'Pavlov';
5 | };
6 |
7 | this.forward = function(lon, lat, xy) {
8 | var phi3, phi5;
9 |
10 | phi3 = lat * lat * lat;
11 | phi5 = phi3 * lat * lat;
12 |
13 | xy[0] = lon;
14 | xy[1] = (lat - 0.1531 / 3 * phi3 - 0.0267 / 5 * phi5);
15 | };
16 | }
17 |
--------------------------------------------------------------------------------
/CanvasMap/src/projections/PutninsP4P.js:
--------------------------------------------------------------------------------
1 | function PutninsP4P() {"use strict";
2 |
3 | var C_x = 0.874038744,
4 | C_y = 3.883251825;
5 |
6 | this.toString = function() {
7 | return 'Putnins P4\'';
8 | };
9 |
10 | this.forward = function(lon, lat, xy) {
11 | lat = Math.asin(0.883883476 * Math.sin(lat));
12 | xy[0] = C_x * lon * Math.cos(lat);
13 | xy[0] /= Math.cos(lat *= 0.333333333333333);
14 | xy[1] = C_y * Math.sin(lat);
15 | };
16 |
17 | this.inverse = function (x, y, lonlat) {
18 | lonlat[1] = Math.asin(y / C_y);
19 | lonlat[0] = x * Math.cos(lonlat[1]) / C_x;
20 | lonlat[1] *= 3.;
21 | lonlat[0] /= Math.cos(lonlat[1]);
22 | lonlat[1] = Math.asin(1.13137085 * Math.sin(lonlat[1]));
23 | };
24 | }
--------------------------------------------------------------------------------
/CanvasMap/src/projections/Robinson.js:
--------------------------------------------------------------------------------
1 | /**
2 | Approximation by Canters & Decleir
3 | */
4 | function Robinson() {"use strict";
5 | this.toString = function() {
6 | return 'Robinson';
7 | };
8 |
9 | this.forward = function(lon, lat, xy) {
10 | var lat2 = lat * lat;
11 | xy[0] = lon * (0.8507 - lat2 * (0.1450 + lat2 * 0.0104));
12 | xy[1] = lat * (0.9642 - lat2 * (0.0013 + lat2 * 0.0129));
13 | };
14 | }
--------------------------------------------------------------------------------
/CanvasMap/src/projections/SineSeries.js:
--------------------------------------------------------------------------------
1 | function SineSeries(p, q) {
2 | "use strict";
3 |
4 | if (arguments.length < 2) {
5 | p = 1.33;
6 | q = 1.135;
7 | }
8 |
9 | var k = q / p;
10 |
11 | this.toString = function () {
12 | return 'Sine Series';
13 | };
14 |
15 | this.setP = function (newP) {
16 | p = parseFloat(newP);
17 | k = q / p;
18 | };
19 |
20 | this.setQ = function (newQ) {
21 | q = parseFloat(newQ);
22 | k = q / p;
23 | };
24 |
25 | this.forward = function (lon, lat, xy) {
26 | var x = k * lon * Math.cos(lat);
27 | lat /= q;
28 | xy[0] = x / Math.cos(lat);
29 | xy[1] = p * Math.sin(lat);
30 | };
31 |
32 | this.inverse = function (x, y, lonlat) {
33 | y /= p;
34 | var lat = Math.asin(y),
35 | c = Math.cos(lat);
36 | lonlat[1] = lat *= q;
37 | lonlat[0] = x / (k * Math.cos(lat)) * c;
38 | };
39 | }
--------------------------------------------------------------------------------
/CanvasMap/src/projections/Sinusoidal.js:
--------------------------------------------------------------------------------
1 | function Sinusoidal() {"use strict";
2 |
3 | this.toString = function() {
4 | return 'Sinusoidal (Equal Area)';
5 | };
6 |
7 | this.forward = function(lon, lat, xy) {
8 | xy[0] = lon * Math.cos(lat);
9 | xy[1] = lat;
10 | };
11 |
12 | this.inverse = function (x, y, lonlat) {
13 | lonlat[0] = x / Math.cos(y);
14 | lonlat[1] = y;
15 | };
16 | }
--------------------------------------------------------------------------------
/CanvasMap/src/projections/Strebe1995.js:
--------------------------------------------------------------------------------
1 | function Strebe1995() {"use strict";
2 |
3 | var kg = 1.35, forward1 = new Eckert4(), inv = new Mollweide(), forward2 = new Hammer();
4 |
5 | this.toString = function() {
6 | return 'Strebe 1995 (Equal Area)';
7 | };
8 |
9 | this.forward = function(lon, lat, xy) {
10 | // Eckert IV
11 | forward1.forward(lon, lat, xy);
12 | // Mollweide
13 | inv.inverse(xy[0] * kg, xy[1] / kg, xy);
14 | // Hammer
15 | forward2.forward(xy[0], xy[1], xy);
16 | xy[0] /= kg;
17 | xy[1] *= kg;
18 | };
19 | }
--------------------------------------------------------------------------------
/CanvasMap/src/projections/Tobler1.js:
--------------------------------------------------------------------------------
1 | function Tobler1() {"use strict";
2 |
3 | this.toString = function() {
4 | return 'Tobler I';
5 | };
6 |
7 | this.forward = function(lon, lat, xy) {
8 | xy[0] = lon;
9 | xy[1] = lat * (1 + lat * lat / 6);
10 | };
11 | }
12 |
--------------------------------------------------------------------------------
/CanvasMap/src/projections/Tobler2.js:
--------------------------------------------------------------------------------
1 | function Tobler2() {"use strict";
2 |
3 | this.toString = function() {
4 | return 'Tobler II';
5 | };
6 |
7 | this.forward = function(lon, lat, xy) {
8 | var latSqr = lat * lat;
9 |
10 | xy[0] = lon;
11 | xy[1] = lat * (1 + latSqr / 6 + latSqr * latSqr / 24);
12 | };
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/CanvasMap/src/projections/TransformableLambert.js:
--------------------------------------------------------------------------------
1 | function TransformableLambert() {"use strict";
2 |
3 | var m, n, CA, CB, poleInsideGraticule, cosLat0;
4 |
5 | function forwardTransformableLambert(lon, lat, xy) {
6 | var sin_O, cos_O, y;
7 |
8 | // FIXME the resulting x coordinate is NaN if lon is Math.PI
9 |
10 | lon *= n;
11 | sin_O = m * Math.sin(lat);
12 | cos_O = Math.sqrt(1 - sin_O * sin_O);
13 | y = 1 + cos_O * Math.cos(lon);
14 | y = Math.sqrt(2 / y);
15 | xy[0] = CA * y * cos_O * Math.sin(lon);
16 | xy[1] = CB * y * sin_O;
17 | }
18 |
19 | function forwardCylindrical(lon, lat, xy) {
20 | xy[0] = lon * cosLat0;
21 | xy[1] = Math.sin(lat) / cosLat0;
22 | }
23 |
24 |
25 | this.initialize = function(lonLimit, latLimit, p) {
26 | var k, d, cylindrical;
27 |
28 | //lonLimit = Math.min(lonLimit, Math.PI);
29 | //latLimit = Math.min(latLimit, Math.PI / 2);
30 |
31 | cylindrical = (lonLimit < 1e-10) && (latLimit < 1e-10);
32 | if (cylindrical) {
33 | this.forward = forwardCylindrical;
34 |
35 | // standard parallel of equal-area cylindrical projection
36 | cosLat0 = Math.sqrt(p / Math.PI);
37 | } else {
38 | this.forward = forwardTransformableLambert;
39 |
40 | // FIXME
41 | lonLimit = Math.max(lonLimit, 1e-10);
42 | latLimit = Math.max(latLimit, 1e-10);
43 |
44 | m = Math.sin(latLimit);
45 | n = lonLimit / Math.PI;
46 | k = Math.sqrt(p * Math.sin(latLimit / 2) / Math.sin(lonLimit / 2));
47 | d = Math.sqrt(m * n);
48 | CA = k / d;
49 | CB = 1 / (k * d);
50 | }
51 |
52 | poleInsideGraticule = (lonLimit === Math.PI);
53 | };
54 |
55 | this.isPoleInsideGraticule = function() {
56 | return poleInsideGraticule;
57 | };
58 |
59 | // FIXME this should not be required, but is currently a fix for the fact that forward() returns
60 | // NaN for the x coordinate if lon is Math.PI
61 | this.getGraticuleWidth = function() {
62 | var xy = [];
63 | this.forward(Math.PI - 1e-6, 0, xy);
64 | return xy[0] * 2;
65 | };
66 |
67 | this.initialize(Math.PI / 2, Math.PI / 4, Math.sqrt(2));
68 | }
--------------------------------------------------------------------------------
/CanvasMap/src/projections/TransverseTransformableLambert.js:
--------------------------------------------------------------------------------
1 | function TransverseTransformableLambert() {"use strict";
2 |
3 | var m, n, CA, CB, azimuthal;
4 |
5 | this.initialize = function(lam1, phi1, p) {
6 | var k, d;
7 |
8 | lam1 = Math.max(lam1, 0.0000001);
9 | phi1 = Math.max(phi1, 0.0000001);
10 |
11 | m = Math.sin(phi1);
12 | n = lam1 / Math.PI;
13 | k = Math.sqrt(p * Math.sin(phi1 / 2) / Math.sin(lam1 / 2));
14 | d = Math.sqrt(m * n);
15 | CA = k / d;
16 | CB = 1 / (k * d);
17 | azimuthal = (p === 1.5);
18 | };
19 |
20 | this.forward = function(lon, lat, xy) {
21 |
22 | var sin_O, cos_O, y, cosLon, cosLat, sinLat;
23 |
24 | // transverse
25 | lon += Math.PI / 2;
26 | cosLon = Math.cos(lon);
27 | cosLat = Math.cos(lat);
28 | lon = Math.atan2(cosLat * Math.sin(lon), Math.sin(lat));
29 | // FIXME adjust longitude
30 | sinLat = -cosLat * cosLon;
31 |
32 | //sinLat = Math.sin(lat);
33 |
34 | lon *= n;
35 | sin_O = m * sinLat;
36 | cos_O = Math.sqrt(1 - sin_O * sin_O);
37 | y = 1 + cos_O * Math.cos(lon);
38 | y = Math.sqrt(2 / y);
39 | xy[1] = -CA * y * cos_O * Math.sin(lon);
40 | xy[0] = CB * y * sin_O;
41 | };
42 |
43 | // FIXME
44 | this.getGraticuleWidth = function() {
45 | return 4;
46 | };
47 |
48 | // FIXME
49 | this.getGraticuleHeight = function() {
50 | return 4;
51 | };
52 |
53 | this.isPoleInsideGraticule = function() {
54 | return azimuthal;
55 | };
56 |
57 | this.initialize(Math.PI / 2, Math.PI / 4, 1.5);
58 | }
--------------------------------------------------------------------------------
/CanvasMap/src/projections/Urmayev2.js:
--------------------------------------------------------------------------------
1 | function Urmayev2() {"use strict";
2 |
3 | this.toString = function() {
4 | return 'Urmayev II';
5 | };
6 |
7 | this.forward = function(lon, lat, xy) {
8 | var latSqr = lat * lat;
9 |
10 | xy[0] = lon;
11 | xy[1] = lat * (1 + 0.1275561329783 * latSqr + 0.0133641090422587 * latSqr * latSqr);
12 | };
13 | }
14 |
--------------------------------------------------------------------------------
/CanvasMap/src/projections/Urmayev3.js:
--------------------------------------------------------------------------------
1 | function Urmayev3() {"use strict";
2 |
3 | this.toString = function() {
4 | return 'Urmayev III';
5 | };
6 |
7 | this.forward = function(lon, lat, xy) {
8 | var a0 = 0.92813433, a2 = 1.11426959;
9 |
10 | xy[0] = lon;
11 | xy[1] = lat * (a0 + a2 / 3 * lat * lat);
12 | };
13 | }
14 |
--------------------------------------------------------------------------------
/CanvasMap/src/projections/Wagner1.js:
--------------------------------------------------------------------------------
1 | function Wagner1() {"use strict";
2 |
3 | var C_x = 0.8773826753,
4 | Cy = 1.139753528477,
5 | n = 0.8660254037844386467637231707,
6 | C_y = Cy / n;
7 |
8 | this.toString = function() {
9 | return 'Wagner I';
10 | };
11 |
12 | this.forward = function(lon, lat, xy) {
13 | var lat = Math.asin(n * Math.sin(lat));
14 | xy[0] = C_x * lon * Math.cos(lat);
15 | xy[1] = C_y * lat;
16 | };
17 |
18 | this.inverse = function (x, y, lonlat) {
19 | y /= C_y;
20 | lonlat[1] = Math.asin(Math.sin(y) / n);
21 | lonlat[0] = x / (C_x * Math.cos(y));
22 | };
23 | }
--------------------------------------------------------------------------------
/CanvasMap/src/projections/Wagner4.js:
--------------------------------------------------------------------------------
1 | function Wagner4() {"use strict";
2 |
3 | var MAX_ITER = 10,
4 | TOLERANCE = 1e-7,
5 | p2 = Math.PI / 3 * 2,
6 | sp = Math.sin(Math.PI / 3),
7 | r = Math.sqrt(Math.PI * 2.0 * sp / (p2 + Math.sin(p2))),
8 | cx = 2. * r / Math.PI,
9 | cy = r / sp,
10 | cp = p2 + Math.sin(p2);
11 |
12 | this.toString = function() {
13 | return 'Wagner IV';
14 | };
15 |
16 | this.forward = function(lon, lat, xy) {
17 | var k = cp * Math.sin(lat), v, i;
18 | for (i = MAX_ITER; i != 0; i--) {
19 | lat -= v = (lat + Math.sin(lat) - k) / (1. + Math.cos(lat));
20 | if (Math.abs(v) < TOLERANCE) {
21 | break;
22 | }
23 | }
24 | if (i == 0) {
25 | lat = (lat < 0.) ? -Math.PI / 2 : Math.PI / 2;
26 | } else {
27 | lat *= 0.5;
28 | }
29 | xy[0] = cx * lon * Math.cos(lat);
30 | xy[1] = cy * Math.sin(lat);
31 | };
32 |
33 | this.inverse = function (x, y, lonlat) {
34 | var lat = Math.asin(y / cy); // FIXME: test for out of bounds
35 | lonlat[0] = x / (cx * Math.cos(lat));
36 | lat += lat;
37 | lonlat[1] = Math.asin((lat + Math.sin(lat)) / cp);// FIXME: test for out of bounds as in Proj4
38 | };
39 | }
--------------------------------------------------------------------------------
/CanvasMap/src/projections/Wagner7.js:
--------------------------------------------------------------------------------
1 | // w = 1: standard Wagner VII
2 | // w = 0: Lambert azimuthal (limiting case, not included here)
3 | // w is optional, default is w = 1
4 | function Wagner7(w) {"use strict";
5 |
6 | var m, n, CA, CB, EPS10 = 1.e-10;
7 |
8 | this.toString = function() {
9 | return 'Wagner VII' + (w !== 1 ? ' Customized' : '');
10 | };
11 |
12 | this.forward = function(lon, lat, xy) {
13 | var sinO, cosO, d;
14 |
15 | sinO = m * Math.sin(lat);
16 | cosO = Math.sqrt(1 - sinO * sinO);
17 | d = Math.sqrt(2. / (1. + cosO * Math.cos(lon *= n)));
18 | xy[0] = CA * d * cosO * Math.sin(lon);
19 | xy[1] = CB * d * sinO;
20 | };
21 |
22 | this.setW = function(weight) {
23 | var k, k2;
24 |
25 | w = weight;
26 | if (w >= 1) {
27 | w = 1;
28 | } else {
29 | if (w < EPS10) {
30 | w = EPS10;
31 | }
32 | }
33 | // constant values
34 | m = Math.sin(65 / 180 * Math.PI) * w + 1 - w;
35 | n = 1 / 3 * w + 1 - w;
36 | k = Math.sqrt((1.0745992166936477 * w + 1 - w) / Math.sin(Math.PI / 2 * n));
37 | k2 = Math.sqrt(m * n);
38 | CA = k / k2;
39 | CB = 1 / (k * k2);
40 | };
41 |
42 | this.setW(arguments.length === 0 ? 1 : w);
43 | }
--------------------------------------------------------------------------------
/CanvasMap/src/projections/WagnerPseudocylindrical.js:
--------------------------------------------------------------------------------
1 | function WagnerPseudocylindrical() {"use strict";
2 |
3 | var m, n, CA, CB, EPS10 = 1.e-10;
4 |
5 | this.toString = function() {
6 | return 'Wagner Pseudocylindrical';
7 | };
8 |
9 | this.forward = function(lon, lat, xy) {
10 | this.setW(61.9 / 180. * Math.PI, 2.03);
11 |
12 | var sinO, cosO, d;
13 |
14 | sinO = m * Math.sin(lat);
15 | cosO = Math.sqrt(1 - sinO * sinO);
16 | d = Math.sqrt(2. / (1. + cosO));
17 | xy[0] = CA * d * cosO * lon;
18 | xy[1] = CB * d * sinO;
19 | };
20 |
21 | this.inverse = function(x, y, lonlat) {
22 | this.setW(65. / 180. * Math.PI, 2.);
23 |
24 | sinO_2 = y / (2 * CB);
25 | cosO_2 = Math.sqrt(1 - sinO_2 * sinO_2);
26 |
27 | lonlat[0] = (x / CA) * cosO_2 / (2 * cosO_2 * cosO_2 - 1);
28 | lonlat[1] = Math.asin(2 * sinO_2 * cosO_2 / m);
29 | };
30 |
31 | this.setW = function(latB, ratio) {
32 |
33 | if (latB < EPS10) {
34 | latB = EPS10;
35 | }
36 |
37 | m = Math.sin(latB);
38 | var k = Math.sqrt(2 * ratio * Math.sin(latB / 2.) / Math.PI);
39 | var k2 = Math.sqrt(m);
40 | CA = k / k2;
41 | CB = 1 / (k * k2);
42 |
43 | console.log('CA', CA);
44 | console.log('CB', CB);
45 | console.log('m', m);
46 | };
47 |
48 | }
--------------------------------------------------------------------------------
/CanvasMap/src/shapefile.js:
--------------------------------------------------------------------------------
1 | // ported from http://code.google.com/p/vanrijkom-flashlibs/ under LGPL v2.1
2 |
3 | function ShpFile(binFile) {
4 |
5 | var src = new BinaryFileWrapper(binFile);
6 |
7 | var t1 = new Date().getTime();
8 | this.header = new ShpHeader(src);
9 |
10 | var t2 = new Date().getTime();
11 | //if (window.console && window.console.log) console.log('parsed header in ' + (t2-t1) + ' ms');
12 |
13 | //if (window.console && window.console.log) console.log('got header, parsing records');
14 |
15 | t1 = new Date().getTime();
16 | this.records = [];
17 | while (true) {
18 | try {
19 | this.records.push(new ShpRecord(src));
20 | } catch (e) {
21 | if (e.id !== ShpError.ERROR_NODATA) {
22 | alert(e);
23 | }
24 | break;
25 | }
26 | }
27 |
28 | t2 = new Date().getTime();
29 | //if (window.console && window.console.log) console.log('parsed records in ' + (t2-t1) + ' ms');
30 |
31 | }
32 |
33 | /**
34 | * The ShpType class is a place holder for the ESRI Shapefile defined
35 | * shape types.
36 | * @author Edwin van Rijkom
37 | *
38 | */
39 | var ShpType = {
40 |
41 | /**
42 | * Unknow Shape Type (for internal use)
43 | */
44 | SHAPE_UNKNOWN : -1,
45 | /**
46 | * ESRI Shapefile Null Shape shape type.
47 | */
48 | SHAPE_NULL : 0,
49 | /**
50 | * ESRI Shapefile Point Shape shape type.
51 | */
52 | SHAPE_POINT : 1,
53 | /**
54 | * ESRI Shapefile PolyLine Shape shape type.
55 | */
56 | SHAPE_POLYLINE : 3,
57 | /**
58 | * ESRI Shapefile Polygon Shape shape type.
59 | */
60 | SHAPE_POLYGON : 5,
61 | /**
62 | * ESRI Shapefile Multipoint Shape shape type
63 | * (currently unsupported).
64 | */
65 | SHAPE_MULTIPOINT : 8,
66 | /**
67 | * ESRI Shapefile PointZ Shape shape type.
68 | */
69 | SHAPE_POINTZ : 11,
70 | /**
71 | * ESRI Shapefile PolylineZ Shape shape type
72 | * (currently unsupported).
73 | */
74 | SHAPE_POLYLINEZ : 13,
75 | /**
76 | * ESRI Shapefile PolygonZ Shape shape type
77 | * (currently unsupported).
78 | */
79 | SHAPE_POLYGONZ : 15,
80 | /**
81 | * ESRI Shapefile MultipointZ Shape shape type
82 | * (currently unsupported).
83 | */
84 | SHAPE_MULTIPOINTZ : 18,
85 | /**
86 | * ESRI Shapefile PointM Shape shape type
87 | */
88 | SHAPE_POINTM : 21,
89 | /**
90 | * ESRI Shapefile PolyLineM Shape shape type
91 | * (currently unsupported).
92 | */
93 | SHAPE_POLYLINEM : 23,
94 | /**
95 | * ESRI Shapefile PolygonM Shape shape type
96 | * (currently unsupported).
97 | */
98 | SHAPE_POLYGONM : 25,
99 | /**
100 | * ESRI Shapefile MultiPointM Shape shape type
101 | * (currently unsupported).
102 | */
103 | SHAPE_MULTIPOINTM : 28,
104 | /**
105 | * ESRI Shapefile MultiPatch Shape shape type
106 | * (currently unsupported).
107 | */
108 | SHAPE_MULTIPATCH : 31
109 |
110 | };
111 |
112 | /**
113 | * Constructor.
114 | * @param src
115 | * @return
116 | * @throws ShpError Not a valid shape file header
117 | * @throws ShpError Not a valid signature
118 | *
119 | */
120 | function ShpHeader(src) {
121 | if (src.getLength() < 100)
122 | alert("Not a valid shape file header (too small)");
123 |
124 | if (src.getSLong() != 9994)
125 | alert("Not a valid signature. Expected 9994");
126 |
127 | // skip 5 integers;
128 | src.position += 5 * 4;
129 |
130 | // read file-length:
131 | this.fileLength = src.getSLong();
132 |
133 | // switch endian:
134 | src.bigEndian = false;
135 |
136 | // read version:
137 | this.version = src.getSLong();
138 |
139 | // read shape-type:
140 | this.shapeType = src.getSLong();
141 |
142 | // read bounds:
143 | this.boundsXY = {
144 | x : src.getDouble(),
145 | y : src.getDouble(),
146 | width : src.getDouble(),
147 | height : src.getDouble()
148 | };
149 |
150 | this.boundsZ = {
151 | x : src.getDouble(),
152 | y : src.getDouble()
153 | };
154 |
155 | this.boundsM = {
156 | x : src.getDouble(),
157 | y : src.getDouble()
158 | };
159 | }
160 |
161 | function ShpRecord(src) {
162 | var availableBytes = src.getLength() - src.position;
163 |
164 | if (availableBytes == 0)
165 | throw (new ShpError("No Data", ShpError.ERROR_NODATA));
166 |
167 | if (availableBytes < 8)
168 | throw (new ShpError("Not a valid record header (too small)"));
169 |
170 | src.bigEndian = true;
171 |
172 | this.number = src.getSLong();
173 | this.contentLength = src.getSLong();
174 | this.contentLengthBytes = this.contentLength * 2 - 4;
175 | src.bigEndian = false;
176 | var shapeOffset = src.position;
177 | this.shapeType = src.getSLong();
178 |
179 | switch(this.shapeType) {
180 | case ShpType.SHAPE_POINT:
181 | this.shape = new ShpPoint(src, this.contentLengthBytes);
182 | break;
183 | case ShpType.SHAPE_POINTZ:
184 | this.shape = new ShpPointZ(src, this.contentLengthBytes);
185 | break;
186 | case ShpType.SHAPE_POLYGON:
187 | this.shape = new ShpPolygon(src, this.contentLengthBytes);
188 | break;
189 | case ShpType.SHAPE_POLYLINE:
190 | this.shape = new ShpPolyline(src, this.contentLengthBytes);
191 | break;
192 | case ShpType.SHAPE_MULTIPATCH:
193 | case ShpType.SHAPE_MULTIPOINT:
194 | case ShpType.SHAPE_MULTIPOINTM:
195 | case ShpType.SHAPE_MULTIPOINTZ:
196 | case ShpType.SHAPE_POINTM:
197 | case ShpType.SHAPE_POLYGONM:
198 | case ShpType.SHAPE_POLYGONZ:
199 | case ShpType.SHAPE_POLYLINEZ:
200 | case ShpType.SHAPE_POLYLINEM:
201 | throw (new ShpError(this.shapeType + " Shape type is currently unsupported by this library"));
202 | break;
203 | default:
204 | throw (new ShpError("Encountered unknown shape type (" + this.shapeType + ")"));
205 | break;
206 | }
207 | }
208 |
209 | function ShpPoint(src, size) {
210 | this.type = ShpType.SHAPE_POINT;
211 | if (src) {
212 | if (src.getLength() - src.position < size)
213 | throw (new ShpError("Not a Point record (too small)"));
214 | this.x = (size > 0) ? src.getDouble() : NaN;
215 | this.y = (size > 0) ? src.getDouble() : NaN;
216 | }
217 | }
218 |
219 | function ShpPointZ(src, size) {
220 | this.type = ShpType.SHAPE_POINTZ;
221 | if (src) {
222 | if (src.getLength() - src.position < size)
223 | throw (new ShpError("Not a Point record (too small)"));
224 | this.x = (size > 0) ? src.getDouble() : NaN;
225 | this.y = (size > 0) ? src.getDouble() : NaN;
226 | this.z = (size > 16) ? src.getDouble() : NaN;
227 | this.m = (size > 24) ? src.getDouble() : NaN;
228 | }
229 | }
230 |
231 | function ShpPolygon(src, size) {
232 | // for want of a super()
233 | ShpPolyline.apply(this, [src, size]);
234 | this.type = ShpType.SHAPE_POLYGON;
235 | }
236 |
237 | function ShpPolyline(src, size) {
238 | this.type = ShpType.SHAPE_POLYLINE;
239 | this.rings = [];
240 | if (src) {
241 | if (src.getLength() - src.position < size)
242 | throw (new ShpError("Not a Polygon record (too small)"));
243 |
244 | src.bigEndian = false;
245 |
246 | this.box = {
247 | x : src.getDouble(),
248 | y : src.getDouble(),
249 | width : src.getDouble(),
250 | height : src.getDouble()
251 | };
252 |
253 | var rc = src.getSLong();
254 | var pc = src.getSLong();
255 |
256 | var ringOffsets = [];
257 | while (rc--) {
258 | var ringOffset = src.getSLong();
259 | ringOffsets.push(ringOffset);
260 | }
261 |
262 | var points = [];
263 | while (pc--) {
264 | points.push(new ShpPoint(src, 16));
265 | }
266 |
267 | // convert points, and ringOffsets arrays to an array of rings:
268 | var removed = 0;
269 | var split;
270 | ringOffsets.shift();
271 | while (ringOffsets.length) {
272 | split = ringOffsets.shift();
273 | this.rings.push(points.splice(0, split - removed));
274 | removed = split;
275 | }
276 | this.rings.push(points);
277 | }
278 | }
279 |
280 | function ShpError(msg, id) {
281 | this.msg = msg;
282 | this.id = id;
283 | this.toString = function() {
284 | return this.msg;
285 | };
286 | }
287 |
288 | ShpError.ERROR_UNDEFINED = 0;
289 | // a 'no data' error is thrown when the byte array runs out of data.
290 | ShpError.ERROR_NODATA = 1;
291 |
--------------------------------------------------------------------------------
/CentralMeridianSlider/UI.js:
--------------------------------------------------------------------------------
1 | /*globals Map, Layer, Graticule, $, NaturalEarth, createMap*/
2 |
3 | var map;
4 |
5 | function getMapScale(map) {"use strict";
6 | var K = 0.90;
7 | return Math.min(map.getMaximumVerticalScale(), map.getMaximumHorizontalScale()) * K;
8 | }
9 |
10 | function writeSliderValue(nbr) {"use strict";
11 | $("#meridianText").text(Math.abs(Math.round(nbr)) + "\u00B0" + (nbr < 0 ? "W" : "E"));
12 | }
13 |
14 | function mouseEventHandler(e) {"use strict";
15 |
16 | var mouseMove, mouseUp, prevMouse;
17 |
18 | prevMouse = {
19 | x : e.clientX,
20 | y : e.clientY
21 | };
22 |
23 | mouseMove = function(e) {
24 | var unitsPerPixel, lon0, dx = e.clientX - prevMouse.x;
25 |
26 | unitsPerPixel = 1 / getMapScale(map);
27 | lon0 = map.getCentralLongitude();
28 | lon0 -= dx * unitsPerPixel;
29 |
30 | map.setCentralLongitude(lon0);
31 |
32 | $("#meridianSlider").slider('value', lon0 / Math.PI * 180);
33 | writeSliderValue(lon0 / Math.PI * 180);
34 |
35 | prevMouse.x = e.clientX;
36 | prevMouse.y = e.clientY;
37 | e.preventDefault();
38 | };
39 |
40 | mouseUp = function(e) {
41 | document.body.style.cursor = null;
42 | document.removeEventListener('mousemove', mouseMove, false);
43 | document.removeEventListener('mouseup', mouseUp, false);
44 | };
45 |
46 | document.body.style.cursor = 'move';
47 | document.addEventListener('mousemove', mouseMove, false);
48 | document.addEventListener('mouseup', mouseUp, false);
49 | }
50 |
51 | function initSlider() {"use strict";
52 |
53 | $(function() {
54 |
55 | function action(event, ui) {
56 | // fix a bug in jQuery slider
57 | // http://stackoverflow.com/questions/9121160/jquery-ui-slider-value-returned-from-slide-event-on-release-is-different-fro
58 | $("#meridianSlider").slider('value', ui.value);
59 | writeSliderValue(ui.value);
60 | map.setCentralLongitude(ui.value / 180 * Math.PI);
61 | }
62 |
63 | // create the meridian slider
64 | $("#meridianSlider").slider({
65 | orientation : "horizontal",
66 | range : "min",
67 | min : -180,
68 | max : 180,
69 | value : 0,
70 | step : 1,
71 | slide : action
72 | });
73 | writeSliderValue(0);
74 | });
75 | }
76 |
77 |
78 | $(window).load(function() {"use strict";
79 |
80 | // currently styling works like this:
81 | // - if there's a fillStyle then it will be filled
82 | // - if there's a strokeStyle then it will be stroked
83 | // - points are always 3px rectangles
84 | // - polylines can't be filled
85 |
86 | var layers = [];
87 |
88 | function Style(strokeStyle, lineWidth, fillStyle) {
89 | if (strokeStyle !== undefined) {
90 | this.strokeStyle = strokeStyle;
91 | this.lineWidth = lineWidth;
92 | }
93 | if (fillStyle !== undefined) {
94 | this.fillStyle = fillStyle;
95 | }
96 | }
97 |
98 | //create the map
99 | layers.push(new Layer("../data/ne_110m_coastline", new Style("#888", "1")));
100 | layers.push(new Graticule(new Style("#77b", "1"), 15));
101 |
102 | map = createMap(layers, new NaturalEarth(), $("#mapCanvas")[0], $("#mapCanvas").width(), $("#mapCanvas").height());
103 | $(window).resize(function() {
104 | map.resize($("#mapCanvas").width(), $("#mapCanvas").height());
105 | map.setScale(getMapScale(map));
106 | });
107 |
108 | map.setScale(getMapScale(map));
109 |
110 | // init UI controls
111 | $("#map").bind("mousedown", mouseEventHandler);
112 |
113 | initSlider();
114 |
115 | // ie
116 | $("#mapCanvas")[0].onselectstart = function() {
117 | return false;
118 | };
119 | // mozilla
120 | $("#mapCanvas")[0].onmousedown = function() {
121 | return false;
122 | };
123 |
124 | });
125 |
126 |
--------------------------------------------------------------------------------
/CentralMeridianSlider/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | MapCanvas Demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/CentralMeridianSlider/static.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #eee;
3 | font: 12px sans-serif;
4 | }
5 |
6 | .map {
7 | height:500px;
8 | width:100%;
9 | padding:0px;
10 | border:0px;
11 | background-color: #fff;
12 | position:relative;
13 | }
14 |
15 | .mapCanvas {
16 | width: 100%;
17 | height: 100%;
18 | position:absolute;
19 | }
20 |
21 | #meridianContainer {
22 | position: relative;
23 | margin-left:auto;
24 | margin-right:auto;
25 | width:380px;
26 | }
27 |
28 | #meridianSlider{
29 | position: relative;
30 | width:300px;
31 | float: left;
32 | }
33 |
34 | #meridianText{
35 | position: relative;
36 | width: 70px;
37 | margin-left: 5px;
38 | float: left;
39 | }
--------------------------------------------------------------------------------
/Hufnagel/UI.js:
--------------------------------------------------------------------------------
1 | /*globals Layer, Graticule, $, Hufnagel, createMap */
2 |
3 | var map;
4 |
5 | function deselectButtons() {
6 | "use strict";
7 | $('#selectable .ui-selected').removeClass('ui-selected');
8 | $('#selectable .ui-selecting').removeClass('ui-selecting');
9 | }
10 |
11 | function updateProjection() {
12 | "use strict";
13 | var projection = new Hufnagel();
14 | projection.setA($("#sliderA").slider("value") / 100);
15 | projection.setB($("#sliderB").slider("value") / 100);
16 | projection.setPsiMax($("#sliderPsi").slider("value") / 180 * Math.PI);
17 | projection.setAspectRatio($("#sliderAspect").slider("value") / 100);
18 | map.setProjection(projection);
19 | if (projection.isGraticuleFolding()) {
20 | $("#foldingWarning").show();
21 | } else {
22 | $("#foldingWarning").hide();
23 | }
24 | }
25 |
26 | function updateSliderTexts() {
27 | "use strict";
28 | var a, b, psi, aspectRatio;
29 | a = $("#sliderA").slider("value") / 100;
30 | b = $("#sliderB").slider("value") / 100;
31 | psi = $("#sliderPsi").slider("value");
32 | aspectRatio = $("#sliderAspect").slider("value") / 100;
33 | $("#sliderAText").text(a.toFixed(4));
34 | $("#sliderBText").text(b.toFixed(4));
35 | $("#sliderPsiText").text(psi.toFixed(0) + "\u00B0");
36 | $("#sliderAspectText").text(aspectRatio.toFixed(2));
37 | }
38 |
39 | function setProjection(selectedItem) {
40 | "use strict";
41 |
42 | var a, b, psi, aspectRatio = 2;
43 |
44 | switch (selectedItem) {
45 | case "projectionMollweide":
46 | a = 0;
47 | b = 0;
48 | psi = 90;
49 | break;
50 | case "projectionEckertIV":
51 | a = 1;
52 | b = 0;
53 | psi = 45;
54 | break;
55 | case "projectionEckertVI":
56 | a = -0.09524;
57 | b = 0.09524;
58 | psi = 60;
59 | break;
60 | case "projectionMollweideWagner":
61 | a = 0;
62 | b = 0;
63 | psi = 60;
64 | break;
65 | case "projectionHufnagel2":
66 | a = 0.05556;
67 | b = -0.05556;
68 | psi = 90;
69 | break;
70 | case "projectionHufnagel3":
71 | a = 0.5;
72 | b = 0.05556;
73 | psi = 90;
74 | break;
75 | case "projectionHufnagel4":
76 | a = 0.08333;
77 | b = -0.08333;
78 | psi = 90;
79 | break;
80 | case "projectionHufnagel7":
81 | a = 0.08333;
82 | b = -0.08333;
83 | psi = 60;
84 | break;
85 | case "projectionHufnagel9":
86 | a = 0.66667;
87 | b = 0.33333;
88 | psi = 45;
89 | break;
90 | case "projectionHufnagel10":
91 | a = -0.66667;
92 | b = 0.66667;
93 | psi = 30;
94 | break;
95 | case "projectionHufnagel11":
96 | a = 0;
97 | b = -0.11111;
98 | psi = 90;
99 | break;
100 | case "projectionHufnagel12":
101 | a = 0;
102 | b = -0.11111;
103 | psi = 40;
104 | aspectRatio = 1 / 0.4;
105 | break;
106 | case "projectionCylindrical":
107 | a = NaN;
108 | b = NaN;
109 | psi = 1;
110 | aspectRatio = NaN;
111 | break;
112 | }
113 |
114 | if (!isNaN(a)) {
115 | $("#sliderA").slider("value", a * 100);
116 | }
117 | if (!isNaN(b)) {
118 | $("#sliderB").slider("value", b * 100);
119 | }
120 | if (!isNaN(psi)) {
121 | $("#sliderPsi").slider("value", psi);
122 | }
123 | if (!isNaN(aspectRatio)) {
124 | $("#sliderAspect").slider("value", aspectRatio * 100);
125 | }
126 |
127 | updateSliderTexts();
128 | updateProjection();
129 | }
130 |
131 | $(function() {
132 | "use strict";
133 |
134 | var mousedown = false, firstRun = true, last_selectedItem_id;
135 | $('#selectable').selectable({
136 | start : function(event, ui) {
137 | mousedown = true;
138 | },
139 | stop : function(event, ui) {
140 | //Here the event ends, so that we can remove the selected class to all but the one we want
141 | $(event.target).children('.ui-selected').not("#" + last_selectedItem_id).removeClass('ui-selected');
142 | mousedown = false;
143 | },
144 | //a special case is the first run,
145 | //$(".ui-selected, .ui-selecting").length is considered to be 2
146 | selecting : function(event, ui) {
147 | if (($(".ui-selected, .ui-selecting").length > 1 ) && firstRun === false) {
148 | $(event.target).children('.ui-selecting').not(':first').removeClass('ui-selecting');
149 | $(ui.selecting).removeClass("ui-selecting");
150 | } else {
151 | setProjection(ui.selecting.id);
152 | last_selectedItem_id = ui.selecting.id;
153 | if (firstRun) {
154 | firstRun = false;
155 | }
156 | }
157 | }
158 | });
159 | });
160 |
161 | function mouseEventHandler(e) {
162 | "use strict";
163 |
164 | var mouseMove, mouseUp, prevMouse;
165 |
166 | prevMouse = {
167 | x : e.clientX,
168 | y : e.clientY
169 | };
170 |
171 | mouseMove = function(e) {
172 | var unitsPerPixel, lon0, dx = e.clientX - prevMouse.x;
173 |
174 | unitsPerPixel = 1 / map.getScale();
175 | lon0 = map.getCentralLongitude();
176 | lon0 -= dx * unitsPerPixel;
177 | map.setCentralLongitude(lon0);
178 |
179 | prevMouse.x = e.clientX;
180 | prevMouse.y = e.clientY;
181 | e.preventDefault();
182 | };
183 |
184 | mouseUp = function(e) {
185 | document.body.style.cursor = null;
186 | document.removeEventListener('mousemove', mouseMove, false);
187 | document.removeEventListener('mouseup', mouseUp, false);
188 | };
189 |
190 | document.body.style.cursor = 'move';
191 | document.addEventListener('mousemove', mouseMove, false);
192 | document.addEventListener('mouseup', mouseUp, false);
193 | }
194 |
195 | function initASlider() {
196 | "use strict";
197 |
198 | function action(event, ui) {
199 | // fix a bug in jQuery slider
200 | // http://stackoverflow.com/questions/9121160/jquery-ui-slider-value-returned-from-slide-event-on-release-is-different-fro
201 | $("#sliderA").slider('value', ui.value);
202 | updateSliderTexts();
203 | updateProjection();
204 | deselectButtons();
205 | }
206 |
207 | // create the slider
208 | $("#sliderA").slider({
209 | orientation : "horizontal",
210 | range : "min",
211 | min : -100,
212 | max : 100,
213 | value : 0,
214 | step : 0.1,
215 | slide : action
216 | });
217 | }
218 |
219 | function initBSlider() {
220 | "use strict";
221 | function action(event, ui) {
222 | // fix a bug in jQuery slider
223 | // http://stackoverflow.com/questions/9121160/jquery-ui-slider-value-returned-from-slide-event-on-release-is-different-fro
224 | $("#sliderB").slider('value', ui.value);
225 | updateSliderTexts();
226 | updateProjection();
227 | deselectButtons();
228 | }
229 |
230 | // create the slider
231 | $("#sliderB").slider({
232 | orientation : "horizontal",
233 | range : "min",
234 | min : -100,
235 | max : 100,
236 | value : 0,
237 | step : 0.1,
238 | slide : action
239 | });
240 | }
241 |
242 | function initPsiSlider() {
243 | "use strict";
244 | function action(event, ui) {
245 | // fix a bug in jQuery slider
246 | // http://stackoverflow.com/questions/9121160/jquery-ui-slider-value-returned-from-slide-event-on-release-is-different-fro
247 | $("#sliderPsi").slider('value', ui.value);
248 | updateSliderTexts();
249 | updateProjection();
250 | deselectButtons();
251 | }
252 |
253 | // create the slider
254 | $("#sliderPsi").slider({
255 | orientation : "horizontal",
256 | range : "min",
257 | min : 0,
258 | max : 90,
259 | value : 90,
260 | step : 1,
261 | slide : action
262 | });
263 | }
264 |
265 | function initAspectSlider() {
266 | "use strict";
267 | function action(event, ui) {
268 | // fix a bug in jQuery slider
269 | // http://stackoverflow.com/questions/9121160/jquery-ui-slider-value-returned-from-slide-event-on-release-is-different-fro
270 | $("#sliderAspect").slider('value', ui.value);
271 | updateSliderTexts();
272 | updateProjection();
273 | deselectButtons();
274 | }
275 |
276 | // create the slider
277 | $("#sliderAspect").slider({
278 | orientation : "horizontal",
279 | range : "min",
280 | min : 100,
281 | max : 275,
282 | value : 200,
283 | step : 1,
284 | slide : action
285 | });
286 | }
287 |
288 | function getMapScale(map) {
289 | "use strict";
290 |
291 | var K = 0.95, aspectRatio = $("#sliderAspect").slider("value") / 100;
292 | if (aspectRatio > map.getCanvas().width / map.getCanvas().height) {
293 | return map.getCanvas().width / (2 * Math.PI) * K;
294 | }
295 | return map.getCanvas().height / Math.PI * K;
296 | }
297 |
298 |
299 | $(window).load(function() {
300 | "use strict";
301 | // currently styling works like this:
302 | // - if there's a fillStyle then it will be filled
303 | // - if there's a strokeStyle then it will be stroked
304 | // - points are always 3px rectangles
305 | // - polylines can't be filled
306 |
307 | var layers, graticule;
308 |
309 | function Style(strokeStyle, lineWidth, fillStyle) {
310 | if (strokeStyle !== undefined) {
311 | this.strokeStyle = strokeStyle;
312 | this.lineWidth = lineWidth;
313 | }
314 | if (fillStyle !== undefined) {
315 | this.fillStyle = fillStyle;
316 | }
317 | }
318 |
319 | //create the map
320 | graticule = new Graticule(new Style("#77b", "1"), 15, 1);
321 | layers = [];
322 | layers.push(new Layer("../data/ne_110m_coastline", new Style("#888", "1")));
323 | layers.push(graticule);
324 |
325 | map = createMap(layers, new Hufnagel(), $("#mapCanvas")[0], $("#mapCanvas").width(), $("#mapCanvas").height());
326 |
327 | $(window).resize(function() {
328 | map.resize($("#mapCanvas").width(), $("#mapCanvas").height());
329 | map.setScale(getMapScale(map));
330 | });
331 |
332 | $("#map").bind("mousedown", mouseEventHandler);
333 |
334 | initASlider();
335 | initBSlider();
336 | initPsiSlider();
337 | initAspectSlider();
338 | updateSliderTexts();
339 | updateProjection();
340 | map.setScale(getMapScale(map));
341 | });
342 |
--------------------------------------------------------------------------------
/Hufnagel/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Hufnagel Projection Family
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | Hufnagel Equal-Area Pseudocylindrical Map Projection Family
24 |
25 | In 1989, Herbert Hufnagel introduced a generalization of the Mollweide projection, resulting in a family of pseudocylindrical equal-area projections. The Mollweide, Eckert Ⅳ and Wagner Ⅳ projections are members of this family.
26 |
27 |
28 | All projections are equal-area. Some paramter combinations create folding graticules.
29 |
30 |
31 | Source: Hufnagel, H. 1989. Ein System unecht-zylindrischer Kartennetze für Erdkarten. Kartographische Nachrichten, 39(3), 89–96.
32 |
33 |
34 | Programming by Bernie Jenny (Oregon State University and RMIT Melbourne), Bojan Šavrič (Oregon State University and Esri Inc.) and Daniel "daan" Strebe, Mapthematics. © Dec 2014–October 2015.
35 |
36 |
37 |
38 |
39 |
40 |
41 | A
42 |
43 |
44 |
48 |
49 |
50 | B
51 |
52 |
53 |
57 |
58 |
59 | Ψmax
60 |
61 |
65 |
66 |
67 | Length Ratio between Equator and Central Meridian
68 |
69 |
73 |
74 |
75 |
76 | Mollweide
77 |
78 |
79 | Eckert Ⅳ
80 |
81 |
82 | Eckert Ⅵ (approximate)
83 |
84 |
85 | Mollweide-Wagner
86 |
87 |
88 | Hufnagel Ⅱ
89 |
90 |
91 | Hufnagel Ⅲ
92 |
93 |
94 | Hufnagel Ⅳ
95 |
96 |
97 | Hufnagel Ⅶ
98 |
99 |
100 | Hufnagel Ⅸ
101 |
102 |
103 | Hufnagel Ⅹ
104 |
105 |
106 | Hufnagel Ⅺ
107 |
108 |
109 | Hufnagel Ⅻ
110 |
111 |
112 | Cylindrical Equal-Area
113 |
114 |
115 |
116 |
117 | Graticule is folding.
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
--------------------------------------------------------------------------------
/Hufnagel/projection-selection-list.css:
--------------------------------------------------------------------------------
1 | #foldingWarning {
2 | color:red;
3 | margin-top: 12px;
4 | margin: 6px;
5 | display: none;
6 | }
7 |
8 | #StParallel {
9 | margin-bottom: 30px;
10 | }
11 |
12 | #selectable {
13 | list-style-type: none;
14 | margin: 0;
15 | padding: 0;
16 | }
17 | #feedback {
18 | font-size: 1.4em;
19 | }
20 | #selectable .ui-selecting {
21 | background: #BBBBBB;
22 | }
23 | #selectable .ui-selected {
24 | background: #BBBBBB;
25 | color: white;
26 | }
27 | #selectable li {
28 | margin-top: 3px;
29 | margin-bottom: 3px;
30 | padding: 0.4em;
31 | width: 195px;
32 | height: 18px;
33 | }
--------------------------------------------------------------------------------
/LambertTransition/UI.js:
--------------------------------------------------------------------------------
1 | /*globals Layer, Graticule, $, TransformableLambert, createMap */
2 |
3 | var map,
4 |
5 | //true when projection is animated
6 | animateFlag = false,
7 |
8 | // id for stopping the animation
9 | animationIntervalId,
10 |
11 | // frames per second for animations
12 | ANIMATION_FPS = 20,
13 |
14 | // the duration of an animation in seconds
15 | ANIMATION_DURATION = 1,
16 |
17 | // the number of frames for one animation
18 | ANIMATION_NBR_FRAMES = Math.round(ANIMATION_DURATION * 1000 / ANIMATION_FPS);
19 |
20 | function selectSelectableElement(selectableContainer, elementsToSelect) {"use strict";
21 | // add unselecting class to all elements in the styleboard canvas except the ones to select
22 | $(".ui-selected", selectableContainer).not(elementsToSelect).removeClass("ui-selected").addClass("ui-unselecting");
23 |
24 | // add ui-selecting class to the elements to select
25 | $(elementsToSelect).not(".ui-selected").addClass("ui-selecting");
26 |
27 | // trigger the mouse stop event (this will select all .ui-selecting elements, and deselect all .ui-unselecting elements)
28 | $('#selectable').selectable("option", "stop");
29 | }
30 |
31 | function updateProjection() {"use strict";
32 | var lat0, w, lam1, phi1, pCyl, p, phi0, projection;
33 |
34 | if (!animateFlag) {
35 | if ($("#sliderBoundingParallel").slider("value") === 90 && $("#sliderBoundingMeridian").slider("value") === 180 && $("#sliderEquator").slider("value") === 1.41) {
36 | selectSelectableElement($("#selectable"), $("li:first", "#selectable"));
37 | } else if ($("#sliderBoundingParallel").slider("value") === 90 && $("#sliderBoundingMeridian").slider("value") === 90 && $("#sliderEquator").slider("value") === 2) {
38 | selectSelectableElement($("#selectable"), $("li:nth-child(2)", "#selectable"));
39 | } else if ($("#sliderBoundingParallel").slider("value") === 90 && $("#sliderBoundingMeridian").slider("value") === 45 && $("#sliderEquator").slider("value") === 2.16) {
40 | selectSelectableElement($("#selectable"), $("li:nth-child(3)", "#selectable"));
41 | } else if ($("#sliderBoundingParallel").slider("value") === 90 && $("#sliderBoundingMeridian").slider("value") === 0 && $("#sliderEquator").slider("value") === 2.22) {
42 | selectSelectableElement($("#selectable"), $("li:nth-child(4)", "#selectable"));
43 | } else if ($("#sliderBoundingParallel").slider("value") === 65 && $("#sliderBoundingMeridian").slider("value") === 60 && $("#sliderEquator").slider("value") === 2) {
44 | selectSelectableElement($("#selectable"), $("li:nth-child(5)", "#selectable"));
45 | } else if ($("#sliderBoundingParallel").slider("value") === 0 && $("#sliderBoundingMeridian").slider("value") === 0 && $("#sliderEquator").slider("value") === 3.14) {
46 | selectSelectableElement($("#selectable"), $("li:nth-child(6)", "#selectable"));
47 | } else if ($("#sliderBoundingParallel").slider("value") === 62 && $("#sliderBoundingMeridian").slider("value") === 0 && $("#sliderEquator").slider("value") === 2.03) {
48 | selectSelectableElement($("#selectable"), $("li:last", "#selectable"));
49 | } else {
50 | $('#selectable .ui-selected').removeClass('ui-selected');
51 | $('#selectable .ui-selecting').removeClass('ui-selecting');
52 | }
53 | }
54 |
55 | p = $("#sliderEquator").slider("value");
56 | phi1 = $("#sliderBoundingParallel").slider("value") / 180 * Math.PI;
57 | lam1 = $("#sliderBoundingMeridian").slider("value") / 180 * Math.PI;
58 |
59 | projection = new TransformableLambert();
60 |
61 | phi0 = Math.acos(Math.sqrt(p / Math.PI));
62 | phi0 = phi0 * 180 / Math.PI;
63 |
64 | if (parseFloat(phi1.toFixed(1)) === 0 && parseFloat(lam1.toFixed(1)) === 0) {
65 | if (parseFloat(p.toFixed(2)) < 3.14) {
66 | //$('#StParallel').show();
67 | $("#StParallel").html('Cylindrical projection with standard parallels at ±' + phi0.toFixed(1) + "\u00B0" + ".");
68 | } else if (parseFloat(p.toFixed(2)) === 3.14) {
69 | //$('#StParallel').show();
70 | $("#StParallel").html('Cylindrical projection with standard parallels at ' + "0" + "\u00B0" + ".");
71 | } else if (parseFloat(p.toFixed(2)) > 3.14) {
72 | //$('#StParallel').hide();
73 | $("#StParallel").html(' ');
74 | }
75 | } else {
76 | //$('#StParallel').hide();
77 | $("#StParallel").html(' ');
78 | }
79 | projection.initialize(lam1, phi1, p);
80 | map.setProjection(projection);
81 | }
82 |
83 | function updateSliderTexts() {"use strict";
84 | $("#sliderBoundingParallelText").text($("#sliderBoundingParallel").slider('value').toFixed(0) + "\u00B0");
85 | $("#sliderBoundingMeridianText").text($("#sliderBoundingMeridian").slider('value').toFixed(0) + "\u00B0");
86 | $("#sliderEquatorText").text($("#sliderEquator").slider('value').toFixed(2));
87 | }
88 |
89 | function animate(targetBoundingParallel, targetBoundingMeridian, targetEquator) {"use strict";
90 |
91 | var dBoundingParallel, dBoundingMeridian, dEquator, frameCounter = 1;
92 | function animateSliders() {
93 |
94 | $("#sliderBoundingParallel").slider("value", targetBoundingParallel - (ANIMATION_NBR_FRAMES - frameCounter) * dBoundingParallel);
95 | $("#sliderBoundingMeridian").slider("value", targetBoundingMeridian - (ANIMATION_NBR_FRAMES - frameCounter) * dBoundingMeridian);
96 | $("#sliderEquator").slider("value", targetEquator - (ANIMATION_NBR_FRAMES - frameCounter) * dEquator);
97 |
98 | //console.log(targetBoundingParallel - (ANIMATION_NBR_FRAMES - frameCounter) * dBoundingParallel, targetBoundingMeridian - (ANIMATION_NBR_FRAMES - frameCounter) * dBoundingMeridian, targetEquator - (ANIMATION_NBR_FRAMES - frameCounter) * dEquator);
99 |
100 | updateSliderTexts();
101 | updateProjection();
102 |
103 | frameCounter += 1;
104 | if (frameCounter > ANIMATION_NBR_FRAMES) {
105 | clearInterval(animationIntervalId);
106 | }
107 | }
108 |
109 | // compute the change/step for each slider
110 | dBoundingParallel = (targetBoundingParallel - $("#sliderBoundingParallel").slider("value") ) / ANIMATION_NBR_FRAMES;
111 | dBoundingMeridian = (targetBoundingMeridian - $("#sliderBoundingMeridian").slider("value") ) / ANIMATION_NBR_FRAMES;
112 | dEquator = (targetEquator - $("#sliderEquator").slider("value") ) / ANIMATION_NBR_FRAMES;
113 |
114 | // calls projection at specified intervals (milliseconds)
115 | animationIntervalId = setInterval(animateSliders, 1 / ANIMATION_FPS);
116 | }
117 |
118 | function animateHelper(selectedItem) {"use strict";
119 |
120 | var targetBoundingParallel, targetBoundingMeridian, targetEquator;
121 |
122 | animateFlag = true;
123 | clearInterval(animationIntervalId);
124 | //selectedItem = $('#selectable .ui-selecting').attr('id');
125 | //selectedItem = $('#selectable .ui-selected').attr('id');
126 |
127 | if (selectedItem === "projectionLambertAzimuthalEA") {
128 | targetBoundingParallel = 90;
129 | targetBoundingMeridian = 180;
130 | targetEquator = Math.sqrt(2);
131 | } else if (selectedItem === "projectionHammer") {
132 | targetBoundingParallel = 90;
133 | targetBoundingMeridian = 90;
134 | targetEquator = 2;
135 | } else if (selectedItem === "projectionEckertGreifendorff") {
136 | targetBoundingParallel = 90;
137 | targetBoundingMeridian = 45;
138 | targetEquator = 4 * Math.sqrt(2) * Math.sin(22.5 * Math.PI / 180);
139 | } else if (selectedItem === "projectionQuarticAuthalic") {
140 | targetBoundingParallel = 90;
141 | targetBoundingMeridian = 0;
142 | targetEquator = (Math.sqrt(2) / 2) * Math.PI;
143 | } else if (selectedItem === "projectionWagnerVII") {
144 | targetBoundingParallel = 65;
145 | targetBoundingMeridian = 60;
146 | targetEquator = 2;
147 | } else if (selectedItem === "projectionLambertCylindricalEA") {
148 | targetBoundingParallel = 0;
149 | targetBoundingMeridian = 0;
150 | targetEquator = Math.PI;
151 | } else if (selectedItem === "projectionPseudocylindrical") {
152 | targetBoundingParallel = 62;
153 | targetBoundingMeridian = 0;
154 | targetEquator = 2.03;
155 | }
156 |
157 | animate(targetBoundingParallel, targetBoundingMeridian, targetEquator);
158 | }
159 |
160 | $(function() {"use strict";
161 |
162 | var mousedown = false, firstRun = true, last_selectedItem_id;
163 | $('#selectable').selectable({
164 | start : function(event, ui) {
165 | mousedown = true;
166 | },
167 | stop : function(event, ui) {
168 | //Here the event ends, so that we can remove the selected class to all but the one we want
169 | $(event.target).children('.ui-selected').not("#" + last_selectedItem_id).removeClass('ui-selected');
170 | mousedown = false;
171 | },
172 | //a special case is the first run,
173 | //$(".ui-selected, .ui-selecting").length is considered to be 2
174 | selecting : function(event, ui) {
175 | if (($(".ui-selected, .ui-selecting").length > 1 ) && firstRun === false) {
176 | $(event.target).children('.ui-selecting').not(':first').removeClass('ui-selecting');
177 | $(ui.selecting).removeClass("ui-selecting");
178 | } else {
179 | animateHelper(ui.selecting.id);
180 | last_selectedItem_id = ui.selecting.id;
181 | if (firstRun) {
182 | firstRun = false;
183 | }
184 | }
185 | }
186 | });
187 | });
188 |
189 | function mouseEventHandler(e) {"use strict";
190 |
191 | var mouseMove, mouseUp, prevMouse;
192 |
193 | prevMouse = {
194 | x : e.clientX,
195 | y : e.clientY
196 | };
197 |
198 | mouseMove = function(e) {
199 | var unitsPerPixel, lon0, dx = e.clientX - prevMouse.x;
200 |
201 | unitsPerPixel = 1 / map.getScale();
202 | lon0 = map.getCentralLongitude();
203 | lon0 -= dx * unitsPerPixel;
204 | map.setCentralLongitude(lon0);
205 |
206 | prevMouse.x = e.clientX;
207 | prevMouse.y = e.clientY;
208 | e.preventDefault();
209 | };
210 |
211 | mouseUp = function(e) {
212 | document.body.style.cursor = null;
213 | document.removeEventListener('mousemove', mouseMove, false);
214 | document.removeEventListener('mouseup', mouseUp, false);
215 | };
216 |
217 | document.body.style.cursor = 'move';
218 | document.addEventListener('mousemove', mouseMove, false);
219 | document.addEventListener('mouseup', mouseUp, false);
220 | }
221 |
222 | function initBoundingParallelSlider() {"use strict";
223 |
224 | function action(event, ui) {
225 | //if animation is going on, interrupt it!
226 | clearInterval(animationIntervalId);
227 | animateFlag = false;
228 |
229 | // fix a bug in jQuery slider
230 | // http://stackoverflow.com/questions/9121160/jquery-ui-slider-value-returned-from-slide-event-on-release-is-different-fro
231 | $("#sliderBoundingParallel").slider('value', ui.value);
232 | updateSliderTexts();
233 | updateProjection();
234 | }
235 |
236 | // create the slider
237 | $("#sliderBoundingParallel").slider({
238 | orientation : "horizontal",
239 | range : "min",
240 | min : 0,
241 | max : 90,
242 | value : 90,
243 | step : 1,
244 | slide : action
245 | });
246 | }
247 |
248 | function initBoundingMeridianSlider() {"use strict";
249 | function action(event, ui) {
250 | //if animation is going on, interrupt it!
251 | clearInterval(animationIntervalId);
252 | animateFlag = false;
253 |
254 | // fix a bug in jQuery slider
255 | // http://stackoverflow.com/questions/9121160/jquery-ui-slider-value-returned-from-slide-event-on-release-is-different-fro
256 | $("#sliderBoundingMeridian").slider('value', ui.value);
257 | updateSliderTexts();
258 | updateProjection();
259 | }
260 |
261 | // create the slider
262 | $("#sliderBoundingMeridian").slider({
263 | orientation : "horizontal",
264 | range : "min",
265 | min : 0,
266 | max : 180,
267 | value : 180,
268 | step : 1,
269 | slide : action
270 | });
271 | }
272 |
273 | function initEquatorSlider() {"use strict";
274 | function action(event, ui) {
275 | //if animation is going on, interrupt it!
276 | clearInterval(animationIntervalId);
277 | animateFlag = false;
278 |
279 | // fix a bug in jQuery slider
280 | // http://stackoverflow.com/questions/9121160/jquery-ui-slider-value-returned-from-slide-event-on-release-is-different-fro
281 | $("#sliderEquator").slider('value', ui.value);
282 | updateSliderTexts();
283 | updateProjection();
284 | }
285 |
286 | // create the slider
287 | $("#sliderEquator").slider({
288 | orientation : "horizontal",
289 | range : "min",
290 | min : 1,
291 | max : 3.5,
292 | value : 1.41,
293 | step : 0.01,
294 | slide : action
295 | });
296 | }
297 |
298 | function getMapScale(map) {"use strict";
299 |
300 | // a reduction factor such that the Lambert Cylindrical fits horizontally
301 | var K = 0.62,
302 | // use the Lambert azimuthal as a reference to compute the map scale
303 | // otherwise different scales would result depending on the chosen parameters, which would
304 | // result in abrupt changes when the canvas is resized.
305 | // the graticule diameter of the Lambert azimuthal is 4
306 | hScale = map.getCanvas().width / 4, vScale = map.getCanvas().height / 4;
307 | return Math.min(vScale, hScale) * K;
308 | }
309 |
310 |
311 | $(window).load(function() {"use strict";
312 | // currently styling works like this:
313 | // - if there's a fillStyle then it will be filled
314 | // - if there's a strokeStyle then it will be stroked
315 | // - points are always 3px rectangles
316 | // - polylines can't be filled
317 |
318 | var layers, graticule;
319 |
320 | function Style(strokeStyle, lineWidth, fillStyle) {
321 | if (strokeStyle !== undefined) {
322 | this.strokeStyle = strokeStyle;
323 | this.lineWidth = lineWidth;
324 | }
325 | if (fillStyle !== undefined) {
326 | this.fillStyle = fillStyle;
327 | }
328 | }
329 |
330 | //create the map
331 | graticule = new Graticule(new Style("#77b", "1"), 15);
332 | layers = [];
333 | layers.push(new Layer("../data/ne_110m_coastline", new Style("#888", "1")));
334 | layers.push(graticule);
335 |
336 | map = createMap(layers, new TransformableLambert(), $("#mapCanvas")[0], $("#mapCanvas").width(), $("#mapCanvas").height());
337 |
338 | $(window).resize(function() {
339 | map.resize($("#mapCanvas").width(), $("#mapCanvas").height());
340 | map.setScale(getMapScale(map));
341 | });
342 |
343 | $("#map").bind("mousedown", mouseEventHandler);
344 |
345 | initBoundingParallelSlider();
346 | initBoundingMeridianSlider();
347 | initEquatorSlider();
348 | updateSliderTexts();
349 | updateProjection();
350 | map.setScale(getMapScale(map));
351 | });
352 |
--------------------------------------------------------------------------------
/LambertTransition/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Wagner's Transformation for the Lambert Azimuthal Equal-Area Projection
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | Wagner's Transformation for the Lambert Azimuthal Equal-Area Projection
24 |
25 |
26 |
27 |
28 |
29 | Bounding Parallel
30 |
31 |
32 |
36 |
37 |
38 | Bounding Meridian
39 |
40 |
41 |
45 |
46 |
47 | Equator / Central Meridian Ratio
48 |
49 |
50 |
54 |
55 |
56 |
57 |
58 | Lambert Azimuthal
59 |
60 |
61 | Hammer
62 |
63 |
64 | Eckert-Greifendorff
65 |
66 |
67 | Quartic Authalic
68 |
69 |
70 | Wagner VII
71 |
72 |
73 | Lambert Cylindrical
74 |
75 |
76 | Pseudocylindrical
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/LambertTransition/projection-selection-list.css:
--------------------------------------------------------------------------------
1 | #StParallel {
2 | margin-bottom: 30px;
3 | }
4 |
5 | #selectable {
6 | list-style-type: none;
7 | margin: 0;
8 | padding: 0;
9 | }
10 | #feedback {
11 | font-size: 1.4em;
12 | }
13 | #selectable .ui-selecting {
14 | background: #BBBBBB;
15 | }
16 | #selectable .ui-selected {
17 | background: #BBBBBB;
18 | color: white;
19 | }
20 | #selectable li {
21 | margin-top: 3px;
22 | margin-bottom: 3px;
23 | padding: 0.4em;
24 | width: 195px;
25 | height: 18px;
26 | }
--------------------------------------------------------------------------------
/MillerTransformation/UI.js:
--------------------------------------------------------------------------------
1 | /*globals Map, Layer, Graticule, $, MillerTransformation, createMap*/
2 |
3 | var map;
4 |
5 | function getMapScale(map) {"use strict";
6 | var K = 0.90;
7 | return Math.min(map.getMaximumVerticalScale(), map.getMaximumHorizontalScale()) * K;
8 | }
9 |
10 | function mouseEventHandler(e) {"use strict";
11 |
12 | var mouseMove, mouseUp, prevMouse;
13 |
14 | prevMouse = {
15 | x : e.clientX,
16 | y : e.clientY
17 | };
18 |
19 | mouseMove = function(e) {
20 | var unitsPerPixel, lon0, dx = e.clientX - prevMouse.x;
21 |
22 | unitsPerPixel = 1 / getMapScale(map);
23 | lon0 = map.getCentralLongitude();
24 | lon0 -= dx * unitsPerPixel;
25 | map.setCentralLongitude(lon0);
26 | prevMouse.x = e.clientX;
27 | prevMouse.y = e.clientY;
28 | e.preventDefault();
29 | };
30 |
31 | mouseUp = function(e) {
32 | document.body.style.cursor = null;
33 | document.removeEventListener('mousemove', mouseMove, false);
34 | document.removeEventListener('mouseup', mouseUp, false);
35 | };
36 |
37 | document.body.style.cursor = 'move';
38 | document.addEventListener('mousemove', mouseMove, false);
39 | document.addEventListener('mouseup', mouseUp, false);
40 | }
41 |
42 | function initSliders() {"use strict";
43 |
44 | // M slider
45 | $(function() {
46 |
47 | function action(event, ui) {
48 | // fix a bug in jQuery slider
49 | // http://stackoverflow.com/questions/9121160/jquery-ui-slider-value-returned-from-slide-event-on-release-is-different-fro
50 | $("#mSlider").slider('value', ui.value);
51 | $("#mText").text("m: " + ui.value);
52 | $("#aspectText").text("Width:Height 1:" + (map.getGraticuleHeight() / map.getGraticuleWidth()).toFixed(3));
53 | map.getProjection().setM(ui.value);
54 | map.render();
55 | }
56 |
57 | // create the meridian slider
58 | $("#mSlider").slider({
59 | orientation : "horizontal",
60 | range : "min",
61 | min : 0.5,
62 | max : 5,
63 | value : 1.5,
64 | step : 0.01,
65 | slide : action
66 | });
67 | $("#mText").text("m: " + 1.5);
68 | });
69 |
70 | // N slider
71 | $(function() {
72 |
73 | function action(event, ui) {
74 | // fix a bug in jQuery slider
75 | // http://stackoverflow.com/questions/9121160/jquery-ui-slider-value-returned-from-slide-event-on-release-is-different-fro
76 | $("#nSlider").slider('value', ui.value);
77 | $("#nText").text("n: " + ui.value);
78 | $("#aspectText").text("Width:Height 1:" + (map.getGraticuleHeight() / map.getGraticuleWidth()).toFixed(3));
79 | map.getProjection().setN(ui.value);
80 | map.render();
81 | }
82 |
83 | // create the meridian slider
84 | $("#nSlider").slider({
85 | orientation : "horizontal",
86 | range : "min",
87 | min : 1,
88 | max : 5,
89 | value : 1.5,
90 | step : 0.01,
91 | slide : action
92 | });
93 | $("#nText").text("n: " + 1.5);
94 | });
95 | }
96 |
97 |
98 | $(window).load(function() {"use strict";
99 |
100 | // currently styling works like this:
101 | // - if there's a fillStyle then it will be filled
102 | // - if there's a strokeStyle then it will be stroked
103 | // - points are always 3px rectangles
104 | // - polylines can't be filled
105 |
106 | var layers = [];
107 |
108 | function Style(strokeStyle, lineWidth, fillStyle) {
109 | if (strokeStyle !== undefined) {
110 | this.strokeStyle = strokeStyle;
111 | this.lineWidth = lineWidth;
112 | }
113 | if (fillStyle !== undefined) {
114 | this.fillStyle = fillStyle;
115 | }
116 | }
117 |
118 | //create the map
119 | layers.push(new Layer("../data/ne_110m_coastline", new Style("#888", "1")));
120 | layers.push(new Graticule(new Style("#77b", "1"), 15));
121 |
122 | map = createMap(layers, new MillerTransformation(), $("#mapCanvas")[0], $("#mapCanvas").width(), $("#mapCanvas").height());
123 | $(window).resize(function() {
124 | map.resize($("#mapCanvas").width(), $("#mapCanvas").height());
125 | map.setScale(getMapScale(map));
126 | });
127 |
128 | map.setScale(getMapScale(map));
129 |
130 | // init UI controls
131 | $("#map").bind("mousedown", mouseEventHandler);
132 |
133 | initSliders();
134 |
135 | // ie
136 | $("#mapCanvas")[0].onselectstart = function() {
137 | return false;
138 | };
139 | // mozilla
140 | $("#mapCanvas")[0].onmousedown = function() {
141 | return false;
142 | };
143 |
144 | });
145 |
146 |
--------------------------------------------------------------------------------
/MillerTransformation/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | MapCanvas Demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Miller's Transformation for Compromise Cylindrical Projections
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/MillerTransformation/static.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #eee;
3 | font: 12px sans-serif;
4 | }
5 |
6 | .map {
7 | height:500px;
8 | width:100%;
9 | padding:0px;
10 | border:0px;
11 | background-color: #fff;
12 | position:relative;
13 | }
14 |
15 | .mapCanvas {
16 | width: 100%;
17 | height: 100%;
18 | position:absolute;
19 | }
20 |
21 | #meridianContainer {
22 | position: relative;
23 | margin-left:auto;
24 | margin-right:auto;
25 | width:380px;
26 | }
27 |
28 | #meridianSlider{
29 | position: relative;
30 | width:300px;
31 | float: left;
32 | }
33 |
34 | #meridianText{
35 | position: relative;
36 | width: 70px;
37 | margin-left: 5px;
38 | float: left;
39 | }
40 |
41 | #mSlider {
42 | float: left;
43 | width: 400px;
44 | }
45 |
46 | #nSlider {
47 | float: left;
48 | width: 400px;
49 | }
--------------------------------------------------------------------------------
/ProjectionList/UI.js:
--------------------------------------------------------------------------------
1 | /*globals Map, Layer, Graticule, $, AspectAdaptiveCylindrical, CylindricalStereographic, EqualAreaCylindrical, Equirectangular, createMap*/
2 |
3 | var map;
4 |
5 | function getProjection(projectionName) {
6 | "use strict";
7 | var proj;
8 |
9 | switch (projectionName) {
10 | case "ArdenClose":
11 | return new ArdenClose();
12 | case "Bonne":
13 | return new Bonne();
14 | case "BraunStereographic":
15 | proj = new CylindricalStereographic();
16 | proj.setStandardParallel(0);
17 | return proj;
18 | case "Braun2":
19 | return new Braun2();
20 | case "BSAM":
21 | proj = new CylindricalStereographic();
22 | proj.setStandardParallel(30 / 180 * Math.PI);
23 | return proj;
24 | case "Canters1":
25 | return new Canters1();
26 | case "Canters2":
27 | return new Canters2();
28 | case "CentralCylindrical":
29 | return new CentralCylindrical();
30 | case "CylindricalStereographic":
31 | return new CylindricalStereographic();
32 | case "Eckert4":
33 | return new Eckert4();
34 | case "Eckert6":
35 | return new Eckert6();
36 | case "EqualAreaCylindrical":
37 | return new EqualAreaCylindrical();
38 | case "Equirectangular":
39 | return new Equirectangular();
40 | case "GallIsographic":
41 | proj = new Equirectangular();
42 | proj.setStandardParallel(Math.PI / 4);
43 | return proj;
44 | case "GallPeters":
45 | proj = new EqualAreaCylindrical();
46 | proj.setStandardParallel(Math.PI / 4);
47 | return proj;
48 | case "GallStereographic":
49 | proj = new CylindricalStereographic();
50 | proj.setStandardParallel(Math.PI / 4);
51 | return proj;
52 | case "Hammer":
53 | return new Hammer();
54 | case "Hufnagel":
55 | return new Hufnagel();
56 | case "Kamenetskiy1":
57 | proj = new CylindricalStereographic();
58 | proj.setStandardParallel(55 / 180 * Math.PI);
59 | return proj;
60 | case "Kavrayskiy1":
61 | return new Kavrayskiy1();
62 | case "Kavrayskiy5":
63 | return new Kavrayskiy5();
64 | case "KharchenkoShabanova":
65 | return new KharchenkoShabanova();
66 | case "LambertEqualAreaCylindrical":
67 | return new LambertEqualAreaCylindrical();
68 | case "McBrydeThomas1":
69 | return new McBrydeThomas1();
70 | case "McBrydeThomas2":
71 | return new McBrydeThomas2();
72 | case "Mercator":
73 | return new Mercator();
74 | case "Miller":
75 | return new Miller();
76 | case "Miller2":
77 | return new Miller2();
78 | case "MillerGall":
79 | proj = new CylindricalStereographic();
80 | // standard parallel at 66.16 degrees
81 | proj.setStandardParallel(2 / Math.sqrt(3));
82 | return proj;
83 | case "MillerPerspective":
84 | return new MillerPerspective();
85 | case "Mollweide":
86 | return new Mollweide();
87 | case "NaturalEarth":
88 | return new NaturalEarth();
89 | case "Pavlov":
90 | return new Pavlov();
91 | case "PlateCarree":
92 | proj = new Equirectangular();
93 | proj.setStandardParallel(0);
94 | return proj;
95 | case "PutninsP4P":
96 | return new PutninsP4P();
97 | case "RMillerMinOverallScaleDistortion":
98 | proj = new Equirectangular();
99 | proj.setStandardParallel(37.5 / 180 * Math.PI);
100 | return proj;
101 | case "RMillerMinContinentalScaleDistortion":
102 | proj = new Equirectangular();
103 | proj.setStandardParallel(43.5 / 180 * Math.PI);
104 | return proj;
105 | case "Robinson":
106 | return new Robinson();
107 | case "Sinusoidal":
108 | return new Sinusoidal();
109 | case "SineSeries":
110 | return new SineSeries();
111 | case "Strebe1995":
112 | return new Strebe1995();
113 | case "Tobler1":
114 | return new Tobler1();
115 | case "Tobler2":
116 | return new Tobler2();
117 | case "Urmayev2":
118 | return new Urmayev2();
119 | case "Urmayev3":
120 | return new Urmayev3();
121 | case "Wagner1":
122 | return new Wagner1();
123 | case "Wagner4":
124 | return new Wagner4();
125 | case "Wagner7":
126 | return new Wagner7();
127 | case "WagnerPseudocylindrical":
128 | return new WagnerPseudocylindrical();
129 | case "LambertAzimuthalEqualAreaPolar":
130 | return new LambertAzimuthalEqualAreaPolar();
131 | default:
132 | return null;
133 | }
134 | }
135 |
136 | function isAdaptiveProjectionSelected() {
137 | "use strict";
138 | var selectedOption = $('#projection-menu').find(":selected").text();
139 | return selectedOption.indexOf("Adaptive") !== -1;
140 | }
141 |
142 | function getMapScale(map) {
143 | "use strict";
144 | var K = 0.97;
145 | return Math.min(map.getMaximumVerticalScale(), map.getMaximumHorizontalScale()) * K;
146 | }
147 |
148 | function setStandardParallels(projection, lat0) {
149 | "use strict";
150 | if (typeof projection.setStandardParallel !== 'undefined') {
151 | projection.setStandardParallel(lat0);
152 | }
153 | map.setScale(getMapScale(map));
154 | }
155 |
156 | function writeSliderValue(nbr) {
157 | "use strict";
158 | $("#sliderValueText").text('\xB1' + nbr.toFixed(1) + "\u00B0");
159 | }
160 |
161 | function mouseEventHandler(e) {
162 | "use strict";
163 |
164 | var mouseMove, mouseUp, prevMouse;
165 |
166 | prevMouse = {
167 | x: e.clientX,
168 | y: e.clientY
169 | };
170 |
171 | mouseMove = function (e) {
172 | var unitsPerPixel, lon0, dx = e.clientX - prevMouse.x;
173 |
174 | unitsPerPixel = 1 / map.getScale();
175 | lon0 = map.getCentralLongitude();
176 | lon0 -= dx * unitsPerPixel;
177 | map.setCentralLongitude(lon0);
178 |
179 | prevMouse.x = e.clientX;
180 | prevMouse.y = e.clientY;
181 | e.preventDefault();
182 | };
183 |
184 | mouseUp = function (e) {
185 | document.body.style.cursor = null;
186 | document.removeEventListener('mousemove', mouseMove, false);
187 | document.removeEventListener('mouseup', mouseUp, false);
188 | };
189 |
190 | document.body.style.cursor = 'move';
191 | document.addEventListener('mousemove', mouseMove, false);
192 | document.addEventListener('mouseup', mouseUp, false);
193 | }
194 |
195 | function updateSliderEnabledState() {
196 | "use strict";
197 | var enabled = false;
198 |
199 | if (map.isDrawn() && isAdaptiveProjectionSelected()) {
200 | enabled = true;
201 | writeSliderValue($("#slider").slider("value"));
202 | } else {
203 | $('#sliderValueText').text("-");
204 | }
205 | $('#slider').slider(enabled ? 'enable' : 'disable');
206 | }
207 |
208 | function setMapProjection() {
209 | "use strict";
210 | map.setProjection(getProjection($("#projection-menu").val()));
211 | map.setScale(getMapScale(map));
212 | updateSliderEnabledState();
213 | if (isAdaptiveProjectionSelected()) {
214 | setStandardParallels(map.getProjection(), $("#slider").slider("value") / 100);
215 | }
216 | }
217 |
218 | function initSlider() {
219 | "use strict";
220 |
221 | $(function () {
222 |
223 | function action(event, ui) {
224 | // http://stackoverflow.com/questions/9121160/jquery-ui-slider-value-returned-from-slide-event-on-release-is-different-fro
225 | $("#slider").slider('value', ui.value);
226 | writeSliderValue(ui.value);
227 | setStandardParallels(map.getProjection(), ui.value / 100);
228 | map.render();
229 | }
230 |
231 | // create the slider
232 | $("#slider").slider({
233 | orientation: "horizontal",
234 | range: "min",
235 | min: 0,
236 | max: 90,
237 | value: 57,
238 | step: 1,
239 | slide: action
240 | });
241 | var sliderValue = $("#slider").slider("value");
242 | setStandardParallels(map.getProjection(), sliderValue);
243 | updateSliderEnabledState();
244 | });
245 | }
246 |
247 |
248 | $(window).load(function () {
249 | "use strict";
250 |
251 | // currently styling works like this:
252 | // - if there's a fillStyle then it will be filled
253 | // - if there's a strokeStyle then it will be stroked
254 | // - points are always 3px rectangles
255 | // - polylines can't be filled
256 |
257 | var layers, projection, graticule;
258 |
259 | function Style(strokeStyle, lineWidth, fillStyle) {
260 | if (strokeStyle !== undefined) {
261 | this.strokeStyle = strokeStyle;
262 | this.lineWidth = lineWidth;
263 | }
264 | if (fillStyle !== undefined) {
265 | this.fillStyle = fillStyle;
266 | }
267 | }
268 |
269 | //create the map
270 | graticule = new Graticule(new Style("#77b", "1"), 15);
271 | layers = [];
272 | layers.push(new Layer("../data/ne_110m_coastline", new Style("#888", "1")));
273 | //layers.push(new Layer("../data/cities/cities_2", new Style(undefined, undefined, "#ff0000")));
274 | layers.push(graticule);
275 | projection = getProjection($("#projection-menu").val());
276 | map = createMap(layers, projection, $("#mapCanvas")[0], $("#mapCanvas").width(), $("#mapCanvas").height());
277 |
278 | $(window).resize(function () {
279 | map.resize($("#mapCanvas").width(), $("#mapCanvas").height());
280 | map.setScale(getMapScale(map));
281 | });
282 |
283 | // init UI controls
284 | $("#map").bind("mousedown", mouseEventHandler);
285 | $("#projection-menu").bind("change", setMapProjection);
286 | initSlider();
287 | updateSliderEnabledState();
288 | });
--------------------------------------------------------------------------------
/ProjectionList/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | MapCanvas Demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | A Compilation of Projections Included in CanvasMap
18 |
19 |
20 |
21 |
22 |
74 |
75 |
76 | Standard Parallels
77 |
78 |
79 |
80 |
81 |
82 | -
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | CanvasMap
2 | =========
3 |
4 | CanvasMap is a simple HTML framework to experiment with projection equations.
5 | CanvasMap uses JavaScript, HTML5 Canvas, and JQuery.
6 |
7 | Sample: http://cartography.oregonstate.edu/ScaleAdaptiveWebMapProjections.html#CanvasMapExample
8 |
9 | Programming by Bernhard Jenny and Bojan Savric, College of Earth, Ocean and
10 | Atmospheric Sciences, Oregon State University, and Mostafa El-Fouly, TU Munich.
11 |
12 | Contact: Bernhard Jenny
13 |
14 | License: GNU General Public License Version 2: http://www.gnu.org/licenses/gpl-2.0.html
15 |
16 | ##File structure
17 | CanvasMap contains the core JavaScript files. Code related to the map and projections is
18 | in src/. CanvasMap.js is created by the build.xml ant script. Include this file in the
19 | HTML file.
20 |
21 | ## Demos
22 | All code specific to an application is included in the UI.js files. These files construct
23 | the HTML GUI, provide event handlers and create the map with a projection.
24 |
25 | SimpleMap shows a map with a static projection.
26 | CentralMeridianSlider shows a map and slider to adjust the central meridian.
27 | ProjectionList has a menu to select from various projections.
28 | LambertTransitionSlider illustrates Wagner's transformation applied to the Lambert
29 | azimuthal projection to create a variety of equal-area projections.
30 | MillerTransformation illustrates a transformation of the Mercator projection suggested
31 | by Miller in 1942.
32 | StrebeTransformation illustrates a transformation method invented by D. Strebe.
33 |
34 | ## Programming Model
35 | UI.js:
36 | DOM event handlers and construction of the model consisting of a map, one or more layers
37 | and a projection.
38 |
39 | layer.js:
40 | A layer loads a shapefile (points, polylines or polygons), and projects and draws the data.
41 | Polygons are not filled. Shapefiles must use 'geographical' coordinates.
42 |
43 | map.js:
44 | Contains an array of layers, a projection, and scale factor applied before drawing the map.
45 | After the layer, canvas, projection and scale are created, invoke map.load to load the
46 | layer geometry.
47 |
48 | ## Extending CanvasMap with Custom Projections
49 | To extend CanvasMap with an additional projection, duplicate an existing
50 | projection file in the projections folder and change the function name and the included
51 | toString() and forward() functions.
52 |
53 | The forward function receives longitude and latitude values in radians. The projected
54 | coordinates have to be written to the xy array. The xy parameter is only for returning the
55 | projected coordinates and does not contain valid data when the function is called.
56 |
57 | Important: use Apache Ant to concatenate and minify all JavaScript files in the src folder.
58 | The default Ant target creates CanvasMap.js and CanvasMap-min.js. To run Ant,
59 | cd to CanvasMap, then type ant.
60 |
61 | To apply the new projection to the map, change UI.js.
62 | If you use the "ProjectionList" map, add your projection to the getProjection function in
63 | UI.js. Also include your projection in the element in index.html. The value for
64 | the new in the element has to be unique and match the projectionName
65 | parameter of getProjection in UI.js.
66 |
--------------------------------------------------------------------------------
/SimpleMap/UI.js:
--------------------------------------------------------------------------------
1 | /*globals Map, Layer, Graticule, $, NaturalEarth, createMap*/
2 |
3 | var map;
4 |
5 |
6 | function getMapScale(map) {"use strict";
7 | var K = 0.90;
8 | return Math.min(map.getMaximumVerticalScale(), map.getMaximumHorizontalScale()) * K;
9 | }
10 |
11 | function mouseEventHandler(e) {"use strict";
12 |
13 | var mouseMove, mouseUp, prevMouse;
14 |
15 | prevMouse = {
16 | x : e.clientX,
17 | y : e.clientY
18 | };
19 |
20 | mouseMove = function(e) {
21 | var unitsPerPixel, lon0, dx = e.clientX - prevMouse.x;
22 |
23 | unitsPerPixel = 1 / map.getScale();
24 | lon0 = map.getCentralLongitude();
25 | lon0 -= dx * unitsPerPixel;
26 | map.setCentralLongitude(lon0);
27 |
28 | prevMouse.x = e.clientX;
29 | prevMouse.y = e.clientY;
30 | e.preventDefault();
31 | };
32 |
33 | mouseUp = function(e) {
34 | document.body.style.cursor = null;
35 | document.removeEventListener('mousemove', mouseMove, false);
36 | document.removeEventListener('mouseup', mouseUp, false);
37 | };
38 |
39 | document.body.style.cursor = 'move';
40 | document.addEventListener('mousemove', mouseMove, false);
41 | document.addEventListener('mouseup', mouseUp, false);
42 | }
43 |
44 | $(window).load(function() {"use strict";
45 |
46 | // currently styling works like this:
47 | // - if there's a fillStyle then it will be filled
48 | // - if there's a strokeStyle then it will be stroked
49 | // - points are always 3px rectangles
50 | // - polylines can't be filled
51 |
52 | var layers = [];
53 |
54 | function Style(strokeStyle, lineWidth, fillStyle) {
55 | if (strokeStyle !== undefined) {
56 | this.strokeStyle = strokeStyle;
57 | this.lineWidth = lineWidth;
58 | }
59 | if (fillStyle !== undefined) {
60 | this.fillStyle = fillStyle;
61 | }
62 | }
63 |
64 | //create the map
65 | layers.push(new Layer("../data/ne_110m_coastline", new Style("#888", "1")));
66 | layers.push(new Graticule(new Style("#77b", "1"), 15));
67 |
68 | map = createMap(layers, new NaturalEarth(), $("#mapCanvas")[0], $("#mapCanvas").width(), $("#mapCanvas").height());
69 | $(window).resize(function() {
70 | map.resize($("#mapCanvas").width(), $("#mapCanvas").height());
71 | map.setScale(getMapScale(map));
72 | });
73 |
74 | map.setScale(getMapScale(map));
75 |
76 | // init UI controls
77 | $("#map").bind("mousedown", mouseEventHandler);
78 | });
--------------------------------------------------------------------------------
/SimpleMap/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | MapCanvas Demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | CanvasMap: Simple Map Demo
17 | Click and drag to adjust the central meridian
18 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/SineSeries/UI.js:
--------------------------------------------------------------------------------
1 | /*globals Map, Layer, Graticule, $, SineSeries, createMap*/
2 |
3 | var map;
4 |
5 | function getMapScale(map) {
6 | "use strict";
7 | //return 300;
8 | var K = 0.9, s = Math.min(map.getMaximumVerticalScale(), map.getMaximumHorizontalScale());
9 | if (isNaN(s) || s > map.getCanvas().width || s < 5) {
10 | s = 0.5 * map.getCanvas().width / (2 * Math.PI);
11 | }
12 | return s * K;
13 | }
14 |
15 | function writeSliderValues(p, q) {
16 | "use strict";
17 | $("#pSliderText").text(p);
18 | $("#qSliderText").text(q);
19 | }
20 |
21 | function mouseEventHandler(e) {
22 | "use strict";
23 |
24 | var mouseMove, mouseUp, prevMouse;
25 |
26 | prevMouse = {
27 | x: e.clientX,
28 | y: e.clientY
29 | };
30 |
31 | mouseMove = function (e) {
32 | var unitsPerPixel, lon0, dx = e.clientX - prevMouse.x;
33 |
34 | unitsPerPixel = 1 / getMapScale(map);
35 | lon0 = map.getCentralLongitude();
36 | lon0 -= dx * unitsPerPixel;
37 |
38 | map.setCentralLongitude(lon0);
39 |
40 | prevMouse.x = e.clientX;
41 | prevMouse.y = e.clientY;
42 | e.preventDefault();
43 | };
44 |
45 | mouseUp = function (e) {
46 | document.body.style.cursor = null;
47 | document.removeEventListener('mousemove', mouseMove, false);
48 | document.removeEventListener('mouseup', mouseUp, false);
49 | };
50 |
51 | document.body.style.cursor = 'move';
52 | document.addEventListener('mousemove', mouseMove, false);
53 | document.addEventListener('mouseup', mouseUp, false);
54 | }
55 |
56 | function initSliders(p, q) {
57 | "use strict";
58 |
59 | $(function () {
60 |
61 | function sliderAction() {
62 | var sp = $("#pSlider").slider("option", "value"),
63 | sq = $("#qSlider").slider("option", "value");
64 | writeSliderValues(sp, sq);
65 | map.getProjection().setP(sp);
66 | map.getProjection().setQ(sq);
67 | adjustMapSize();
68 | map.render();
69 | }
70 |
71 | function pAction(event, ui) {
72 | // fix a bug in jQuery slider
73 | // http://stackoverflow.com/questions/9121160/jquery-ui-slider-value-returned-from-slide-event-on-release-is-different-fro
74 | $("#pSlider").slider('value', ui.value);
75 | sliderAction();
76 | }
77 |
78 | function qAction(event, ui) {
79 | // fix a bug in jQuery slider
80 | // http://stackoverflow.com/questions/9121160/jquery-ui-slider-value-returned-from-slide-event-on-release-is-different-fro
81 | $("#qSlider").slider('value', ui.value);
82 | sliderAction();
83 | }
84 |
85 | // create the sliders
86 | $("#pSlider").slider({
87 | orientation: "horizontal",
88 | range: "min",
89 | min: 1,
90 | max: 3,
91 | value: p,
92 | step: 0.01,
93 | slide: pAction
94 | });
95 |
96 | $("#qSlider").slider({
97 | orientation: "horizontal",
98 | range: "min",
99 | min: 1,
100 | max: 3,
101 | value: q,
102 | step: 0.005,
103 | slide: qAction
104 | });
105 |
106 | writeSliderValues(p, q);
107 | map.getProjection().setP(p);
108 | map.getProjection().setQ(q);
109 | });
110 | }
111 |
112 | function adjustMapSize() {
113 | var mapHeight = $(window).outerHeight(true) - $("#guiControlsContainer").outerHeight(true) - $("#header").outerHeight(true);
114 | $("#map").height(mapHeight);
115 | map.resize($("#mapCanvas").width(), $("#mapCanvas").height());
116 | map.setScale(getMapScale(map));
117 | }
118 |
119 | function setPQ(p, q) {
120 | map.getProjection().setP(p);
121 | map.getProjection().setQ(q);
122 | $("#pSlider").slider('value', p);
123 | $("#qSlider").slider('value', q);
124 | writeSliderValues(p, q);
125 | adjustMapSize();
126 | map.render();
127 | }
128 |
129 | $(window).load(function () {
130 | "use strict";
131 |
132 | var p = 1.33,
133 | q = 1.135;
134 |
135 | // currently styling works like this:
136 | // - if there's a fillStyle then it will be filled
137 | // - if there's a strokeStyle then it will be stroked
138 | // - points are always 3px rectangles
139 | // - polylines can't be filled
140 |
141 | var layers = [];
142 |
143 | function Style(strokeStyle, lineWidth, fillStyle) {
144 | if (strokeStyle !== undefined) {
145 | this.strokeStyle = strokeStyle;
146 | this.lineWidth = lineWidth;
147 | }
148 | if (fillStyle !== undefined) {
149 | this.fillStyle = fillStyle;
150 | }
151 | }
152 |
153 | //create the map
154 | layers.push(new Layer("../data/ne_110m_coastline", new Style("#888", "1")));
155 | layers.push(new Graticule(new Style("#77b", "1"), 15));
156 |
157 | map = createMap(layers, new SineSeries(p, q), $("#mapCanvas")[0], $("#mapCanvas").width(), $("#mapCanvas").height());
158 | $(window).resize(function () {
159 | adjustMapSize();
160 | });
161 |
162 | map.setScale(getMapScale(map));
163 |
164 | // init UI controls
165 | $("#map").bind("mousedown", mouseEventHandler);
166 |
167 | initSliders(p, q);
168 |
169 | // ie
170 | $("#mapCanvas")[0].onselectstart = function () {
171 | return false;
172 | };
173 | // mozilla
174 | $("#mapCanvas")[0].onmousedown = function () {
175 | return false;
176 | };
177 |
178 | adjustMapSize();
179 | });
180 |
181 |
--------------------------------------------------------------------------------
/SineSeries/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Sine Series
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
p
28 |
29 |
30 |
31 |
32 |
33 |
38 |
39 |
40 | Quartic Authalic
41 | McBryde-Thomas 1
42 | Kavraisky V
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/SineSeries/static.css:
--------------------------------------------------------------------------------
1 | .map {
2 | height: 900px;
3 | width: 100%;
4 | padding: 0px;
5 | border: 0px;
6 | margin: 0px;
7 | background-color: #fff;
8 | position: relative;
9 | }
10 |
11 | body {
12 | background-color: #eee;
13 | font-family: "Lucida Grande", "Open Sans", sans-serif;
14 | font-size: 12px;
15 | padding: 0px;
16 | border: 0px;
17 | margin: 0px;
18 | }
19 |
20 | h1 {
21 | font-family: "Lucida Grande", "Open Sans", sans-serif;
22 | font-size: 14px;
23 | font-weight: bolder;
24 | text-align: center;
25 | }
26 |
27 | .guiTitle{
28 | font-family: "Lucida Grande", "Open Sans", sans-serif;
29 | font-size: 12px;
30 | font-weight: bolder;
31 | margin-bottom: 5px;
32 | }
33 |
34 | .guiGroup {
35 | padding-bottom: 15px;
36 | }
37 |
38 | .mapCanvas {
39 | width: 100%;
40 | height: 100%;
41 | position:absolute;
42 | }
43 |
44 | #guiControlsContainer {
45 | position: relative;
46 | margin-left:auto;
47 | margin-right:auto;
48 | width:380px;
49 | margin-top: 20px;
50 | }
51 |
52 | .slider{
53 | position: relative;
54 | width:300px;
55 | float: left;
56 | }
57 |
58 |
59 | .sliderText{
60 | position: relative;
61 | width: 70px;
62 | margin-left: 5px;
63 | float: left;
64 | }
65 |
66 | #buttonGroup {
67 | margin-top: 15px;
68 | }
69 |
--------------------------------------------------------------------------------
/StrebeTransformation/UI.js:
--------------------------------------------------------------------------------
1 | /*globals Map, Layer, Graticule, $, Strebe1995, createMap*/
2 |
3 | var map;
4 |
5 | function getMapScale(map) {"use strict";
6 | return 80;
7 | //var K = 0.80;
8 | //return Math.min(map.getMaximumVerticalScale(), map.getMaximumHorizontalScale()) * K;
9 | }
10 |
11 | function writeSliderValue(nbr) {"use strict";
12 | $("#sliderText").text(nbr);
13 | }
14 |
15 | function mouseEventHandler(e) {"use strict";
16 |
17 | var mouseMove, mouseUp, prevMouse;
18 |
19 | prevMouse = {
20 | x : e.clientX,
21 | y : e.clientY
22 | };
23 |
24 | mouseMove = function(e) {
25 | var unitsPerPixel, lon0, dx = e.clientX - prevMouse.x;
26 |
27 | unitsPerPixel = 1 / getMapScale(map);
28 | lon0 = map.getCentralLongitude();
29 | lon0 -= dx * unitsPerPixel;
30 |
31 | map.setCentralLongitude(lon0);
32 |
33 | prevMouse.x = e.clientX;
34 | prevMouse.y = e.clientY;
35 | e.preventDefault();
36 | };
37 |
38 | mouseUp = function(e) {
39 | document.body.style.cursor = null;
40 | document.removeEventListener('mousemove', mouseMove, false);
41 | document.removeEventListener('mouseup', mouseUp, false);
42 | };
43 |
44 | document.body.style.cursor = 'move';
45 | document.addEventListener('mousemove', mouseMove, false);
46 | document.addEventListener('mouseup', mouseUp, false);
47 | }
48 |
49 | function initSlider() {"use strict";
50 |
51 | $(function() {
52 |
53 | function action(event, ui) {
54 | // fix a bug in jQuery slider
55 | // http://stackoverflow.com/questions/9121160/jquery-ui-slider-value-returned-from-slide-event-on-release-is-different-fro
56 | $("#slider").slider('value', ui.value);
57 | writeSliderValue(ui.value);
58 | map.getProjection().setScaleFactor(ui.value);
59 | map.render();
60 | }
61 |
62 | // create the slider
63 | $("#slider").slider({
64 | orientation : "horizontal",
65 | range : "min",
66 | min : 0,
67 | max : 5,
68 | value : 1.35,
69 | step : 0.01,
70 | slide : action
71 | });
72 | writeSliderValue(1.35);
73 | });
74 | }
75 |
76 | function getProjection(projectionName) {"use strict";
77 | var proj;
78 |
79 | switch (projectionName) {
80 | case "AlbersConic":
81 | return new AlbersConic();
82 | case "ArdenClose":
83 | return new ArdenClose();
84 | case "Bonne":
85 | return new Bonne();
86 | case "BraunStereographic":
87 | proj = new CylindricalStereographic();
88 | proj.setStandardParallel(0);
89 | return proj;
90 | case "Braun2":
91 | return new Braun2();
92 | case "BSAM":
93 | proj = new CylindricalStereographic();
94 | proj.setStandardParallel(30 / 180 * Math.PI);
95 | return proj;
96 | case "Canters1":
97 | return new Canters1();
98 | case "Canters2":
99 | return new Canters2();
100 | case "CentralCylindrical":
101 | return new CentralCylindrical();
102 | case "CylindricalStereographic":
103 | return new CylindricalStereographic();
104 | case "Eckert4":
105 | return new Eckert4();
106 | case "EqualAreaCylindrical":
107 | return new EqualAreaCylindrical();
108 | case "Equirectangular":
109 | return new Equirectangular();
110 | case "GallIsographic":
111 | proj = new Equirectangular();
112 | proj.setStandardParallel(Math.PI / 4);
113 | return proj;
114 | case "GallPeters":
115 | proj = new EqualAreaCylindrical();
116 | proj.setStandardParallel(Math.PI / 4);
117 | return proj;
118 | case "GallStereographic":
119 | proj = new CylindricalStereographic();
120 | proj.setStandardParallel(Math.PI / 4);
121 | return proj;
122 | case "Hammer":
123 | return new Hammer();
124 | case "Hufnagel":
125 | return new Hufnagel();
126 | case "Kamenetskiy1":
127 | proj = new CylindricalStereographic();
128 | proj.setStandardParallel(55 / 180 * Math.PI);
129 | return proj;
130 | case "Kavrayskiy1":
131 | return new Kavrayskiy1();
132 | case "KharchenkoShabanova":
133 | return new KharchenkoShabanova();
134 | case "LambertEqualAreaCylindrical":
135 | return new LambertEqualAreaCylindrical();
136 | case "Mercator":
137 | return new Mercator();
138 | case "Miller":
139 | return new Miller();
140 | case "Miller2":
141 | return new Miller2();
142 | case "MillerGall":
143 | proj = new CylindricalStereographic();
144 | // standard parallel at 66.16 degrees
145 | proj.setStandardParallel(2 / Math.sqrt(3));
146 | return proj;
147 | case "MillerPerspective":
148 | return new MillerPerspective();
149 | case "Mollweide":
150 | return new Mollweide();
151 | case "NaturalEarth":
152 | return new NaturalEarth();
153 | case "Pavlov":
154 | return new Pavlov();
155 | case "PlateCarree":
156 | proj = new Equirectangular();
157 | proj.setStandardParallel(0);
158 | return proj;
159 | case "RMillerMinOverallScaleDistortion":
160 | proj = new Equirectangular();
161 | proj.setStandardParallel(37.5 / 180 * Math.PI);
162 | return proj;
163 | case "RMillerMinContinentalScaleDistortion":
164 | proj = new Equirectangular();
165 | proj.setStandardParallel(43.5 / 180 * Math.PI);
166 | return proj;
167 | case "Robinson":
168 | return new Robinson();
169 | case "Sinusoidal":
170 | return new Sinusoidal();
171 | case "Strebe1995":
172 | return new Strebe1995();
173 | case "Tobler1":
174 | return new Tobler1();
175 | case "Tobler2":
176 | return new Tobler2();
177 | case "Urmayev2":
178 | return new Urmayev2();
179 | case "Urmayev3":
180 | return new Urmayev3();
181 | case "Wagner7":
182 | return new Wagner7();
183 | case "LambertAzimuthalEqualAreaPolar":
184 | return new LambertAzimuthalEqualAreaPolar();
185 | default:
186 | return null;
187 | }
188 | }
189 |
190 | function setForward1() {"use strict";
191 | map.getProjection().setForward1(getProjection($("#forward1-menu").val()));
192 | map.render();
193 | }
194 |
195 | function setInverse() {"use strict";
196 | map.getProjection().setInverse(getProjection($("#inverse-menu").val()));
197 | map.render();
198 | }
199 |
200 | function setForward2() {"use strict";
201 | map.getProjection().setForward2(getProjection($("#forward2-menu").val()));
202 | map.render();
203 | }
204 |
205 | $(window).load(function() {"use strict";
206 |
207 | // currently styling works like this:
208 | // - if there's a fillStyle then it will be filled
209 | // - if there's a strokeStyle then it will be stroked
210 | // - points are always 3px rectangles
211 | // - polylines can't be filled
212 |
213 | var layers = [];
214 |
215 | function Style(strokeStyle, lineWidth, fillStyle) {
216 | if (strokeStyle !== undefined) {
217 | this.strokeStyle = strokeStyle;
218 | this.lineWidth = lineWidth;
219 | }
220 | if (fillStyle !== undefined) {
221 | this.fillStyle = fillStyle;
222 | }
223 | }
224 |
225 | //create the map
226 | layers.push(new Layer("../data/ne_110m_coastline", new Style("#888", "1")));
227 | layers.push(new Graticule(new Style("#77b", "1"), 15));
228 |
229 | map = createMap(layers, new Strebe1995(), $("#mapCanvas")[0], $("#mapCanvas").width(), $("#mapCanvas").height());
230 | $(window).resize(function() {
231 | map.resize($("#mapCanvas").width(), $("#mapCanvas").height());
232 | map.setScale(getMapScale(map));
233 | });
234 |
235 | map.setScale(getMapScale(map));
236 |
237 | // init UI controls
238 | $("#map").bind("mousedown", mouseEventHandler);
239 |
240 | initSlider();
241 |
242 | // ie
243 | $("#mapCanvas")[0].onselectstart = function() {
244 | return false;
245 | };
246 | // mozilla
247 | $("#mapCanvas")[0].onmousedown = function() {
248 | return false;
249 | };
250 |
251 | $("#forward1-menu").bind("change", setForward1);
252 | $("#inverse-menu").bind("change", setInverse);
253 | $("#forward2-menu").bind("change", setForward2);
254 |
255 | });
256 |
257 |
--------------------------------------------------------------------------------
/StrebeTransformation/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Strebe Transformation
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | First Forward Projection
22 |
65 |
66 |
Inverse Projection
67 |
75 |
76 |
Second Forward Projection
77 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
--------------------------------------------------------------------------------
/StrebeTransformation/static.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #eee;
3 | font: 12px sans-serif;
4 | }
5 |
6 | .map {
7 | height:500px;
8 | width:100%;
9 | padding:0px;
10 | border:0px;
11 | background-color: #fff;
12 | position:relative;
13 | }
14 |
15 | .mapCanvas {
16 | width: 100%;
17 | height: 100%;
18 | position:absolute;
19 | }
20 |
21 | #meridianContainer {
22 | position: relative;
23 | margin-left:auto;
24 | margin-right:auto;
25 | width:380px;
26 | }
27 |
28 | #meridianSlider{
29 | position: relative;
30 | width:300px;
31 | float: left;
32 | }
33 |
34 | #meridianText{
35 | position: relative;
36 | width: 70px;
37 | margin-left: 5px;
38 | float: left;
39 | }
--------------------------------------------------------------------------------
/data/ne_110m_coastline.VERSION.txt:
--------------------------------------------------------------------------------
1 | 2.0.0
--------------------------------------------------------------------------------
/data/ne_110m_coastline.dbf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OSUCartography/CanvasMap/66ee766eae2184f309703902dfe576c1fe9a601c/data/ne_110m_coastline.dbf
--------------------------------------------------------------------------------
/data/ne_110m_coastline.prj:
--------------------------------------------------------------------------------
1 | GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.017453292519943295]]
--------------------------------------------------------------------------------
/data/ne_110m_coastline.shp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OSUCartography/CanvasMap/66ee766eae2184f309703902dfe576c1fe9a601c/data/ne_110m_coastline.shp
--------------------------------------------------------------------------------
/data/ne_110m_coastline.shx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OSUCartography/CanvasMap/66ee766eae2184f309703902dfe576c1fe9a601c/data/ne_110m_coastline.shx
--------------------------------------------------------------------------------