City level rendering in D3 - mapping

I'm trying to render a projection using data from Open Street Maps. I downloaded the Sweden transportation file from here and converted the buildings shapefile to topojson. This is an excerpt:
{"type":"Topology","transform":{"scale":[0.0013154551155115512,0.0013336730673067306],"translate":[11.0320167,55.3382768]},"objects":{"buildings":{"type":"GeometryCollection","geometries":[{"type":"Polygon","arcs":[[0,1,2]]},{"type":null},{"type":"Polygon","arcs":[[3]]}...
I'm aiming to create a map of the buildings in Stockholm and have the following D3 code:
<script>
var width = 960,
height = 500;
var projection = d3.geo.albers()
.translate(width/2, height/2)
.scale(20000)
.rotate([-18.0365, 0])
.center([0, 59.3115]);
var path = d3.geo.path()
.projection(projection);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.json('buildings.json', function(err, city) {
svg.append("path")
.datum(topojson.object(city, city.objects.buildings.geometries))
.attr("d", path);
});
</script>
However, this just loads a blank page and there are no errors in the javascript console. Not sure where to go next, any help will be greatly appreciated.
Thanks,
Tom

Related

KonvasJs: Image height and width lost when saving canvas stage to JSON string

I allow the user to upload an image to the canvas; when creating the Konvas.Image I set the height and width attributes based on the images naturalHeight and naturalWidth.
Upload Code:
function loadImageStep1(src) {
var imgKI = new Konva.Image({
x: 0,
y: 0,
name: 'step1-label',
id: 'step1-label'
});
layer1.add(imgKI);
stage.add(layer1);//Add layer to stage.
var layer1Img = stage.find('.step1-label');
var reader = new FileReader();
reader.onload = function(event) {
img = new Image();
img.onload = function() {
imgKI.image(img);
layer1.draw();
layer1Img.setAttr('width', this.naturalWidth);
layer1Img.setAttr('height', this.naturalHeight);
layer1.draw();
}
img.src = event.target.result;//event.target.result
layer1Img.setAttr('src', event.target.result);
}
reader.readAsDataURL(src);
return false;
}
When I save the canvas to a JSON string the image height and width is gone. I saved the Konva.Image object to a variable and added to the console log to verify that the attributes are present.
Screen Shot of Console Log
Here is the JSON string provided by the stage.toJSON() function; please note that the height and width for the "step1-label" image object is not present.
{"attrs":{"width":500,"height":667,"id":"label-maker","fill":"#ddd"},"className":"Stage","children":[{"attrs":{"id":"background","name":"background"},"className":"Layer","children":[{"attrs":{"width":500,"height":667,"fill":"#ffffff"},"className":"Rect"}]},{"attrs":{"id":"layer1","name":"layer1"},"className":"Layer","children":[{"attrs":{"x":100,"y":180,"name":"step1-label","id":"step1-label","src":"[REMOVED TO MAKE STRING SMALLER]","scaleX":"1.72","scaleY":"1.72"},"className":"Image"}]},{"attrs":{"id":"layer3","name":"layer3"},"className":"Layer","children":[{"attrs":{"id":"txtGroup1","name":"txtGroup1","draggable":true,"x":-5,"y":202},"className":"Group","children":[{"attrs":{"x":250,"y":333.5,"text":"Fine Dinning \nSpecial Preserve","fontSize":"50","fontFamily":"Great Vibes","fill":"#000000","name":"text1","id":"text1","align":"center","lineHeight":"1","fontStyle":"bold","offsetX":203.81988525390625},"className":"Text"}]}]}]}
Here is my work around for this issue. I had to get the image objects and add in the height and width to the JSON string. This is simple because we only allow two images to be added to the canvas.
function saveLabel($title, $continue) {
//Get current width, height and scale settings for the canvas. Use these values to reset once the canvas has beeen saved.
var currentWidth = stage.width();
var currentHeight = stage.height();
var currentXScale = stage.scaleX();
var currentYScale = stage.scaleY();
stage.setWidth(width);
stage.setHeight(height);
stage.scale({ x: 1, y: 1 });
stage.draw();
var $json = stage.toJSON();
//The Konvas library is dropping the height and width of the images when creating the json string, so we have to put them back in...
var layer1Img = stage.find('.step1-label');
var layer2Img = stage.find('.custom');
if (layer1Img.length) {
$json = $json.replace('"id":"step1-label"','"id":"step1-label","height":' + layer1Img[0]["attrs"].height + ',"width":' + layer1Img[0]["attrs"].width);
}
if (layer2Img.length) {
$json = $json.replace('"id":"custom"','"id":"custom","height":' + layer2Img[0]["attrs"].height + ',"width":' + layer2Img[0]["attrs"].width);
}
var $dataURL = stage.toDataURL('image/png');
//Set the canvas back to original settings now that it's been saved.
stage.setWidth(currentWidth);
stage.setHeight(currentHeight);
stage.scale({ x: currentXScale, y: currentYScale });
stage.draw();
saveLabelAjax($json, $dataURL, $title, $continue, label_maker, window.location.hash);
}
Is there something I'm missing in my code; am I setting the image attributes correctly?
I need the image height and width for a rotation function and when I load the canvas from the JSON the rotation functions doesn't work correctly because of this missing data.
I found why you have no width and height in JSON.
While converting a Konva.Node to JSON Konva is trying to make it as minimal as possible. For example, it does NOT save default values (for x and y in will be 0).
In you case width attribute of Konva.Image equals to the natural width of the native image you use. So it is useless in JSON.
When you restore a stage from JSON you will need just load native images and set them to Konva.Image instances.

384x384px tile to diplay at 384x384px on map (retina)

I serve TMS tile with in two flavours: 256px or 384px through renderd option scale=1.5.
With Openlayers 3, the only way I found to display these 384px tiles their original size is to transform the canvas context like this:
map.getViewport().getElementsByTagName('canvas')[0].getContext("2d").setTransform(1.5, 0, 0, 1.5, -w, -h);
I think it's not the proper way to go, so what would be the right one?
I played a bit with a special ol.tilegrid but with no success, see here:
https://jsfiddle.net/yvecai/owwc5bo8/8/
The output I aim for is on the right map.
There is no need to create a special tile grid or to apply any canvas scaling. All you need to do is set the tilePixelRatio of the source properly, which would be 1.5 in your case:
source: new ol.source.XYZ({
url: "http://www5.opensnowmap.org/base_snow_map_high_dpi/{z}/{x}/{y}.png?debug",
attributions: [/* ... */],
tilePixelRatio: 1.5
})
Also note that your expectation of the result is wrong. I updated your fiddle to compare the standard 256px tiles (on the right) with the hidpi 384px tiles (on the left). If you are viewing the fiddle on a hidpi display, you'll notice the difference. https://jsfiddle.net/owwc5bo8/9/
To summarize:
If you want to display high-dpi tiles on a mobile device with good sharpness, use tilePixelRatio :
https://jsfiddle.net/owwc5bo8/9/
If you want to display tiles with a size differnet than 256x256, create a proper ol.tilegrid, and a proper ol.view:
https://jsfiddle.net/owwc5bo8/12/
var extent = ol.proj.get('EPSG:3857').getExtent();
var tileSizePixels = 384;
var tileSizeMtrs = ol.extent.getWidth(extent) / 384;
var resolutions = [];
for (var i = -1; i <= 20; i++) {
resolutions[i] = tileSizeMtrs / (Math.pow(2, i));
}
var tileGrid = new ol.tilegrid.TileGrid({
extent: extent,
resolutions: resolutions,
tileSize: [384, 384]
});
var center = ol.proj.fromLonLat(
ol.proj.toLonLat([2, 49], 'EPSG:4326'),
'EPSG:3857');
var zoom = 5;
var view1 = new ol.View({
center: center,
zoom: zoom,
maxResolution: 40075016.68557849 / 384
});

openlayers-3 precompose, how to do layer clipping with a rectangular geometry

I'm trying to make a simple app doing something similar to the Layer Spy Example, but instead of a circle flowing the mouse pointer, I would like to do the clipping based on a rectangle which is always centered in the map.
(preferably with "fixed" size" i.e. changes "extent" when zooming)
Any help appreciated
Thanks
Frode
You can do it like in the example that you are referring to. But instead of context.arc() use context.rect() with a fixed position to clip the layer. Something like:
imagery.on('precompose', function(event) {
var ctx = event.context;
var pixelRatio = event.frameState.pixelRatio;
ctx.save();
ctx.beginPath();
var x = ctx.canvas.width / 2 - 100;
var y = ctx.canvas.height / 2 - 100;
ctx.rect(x, y, 100, 100);
ctx.clip();
});
http://jsfiddle.net/eo1c1x78/

Is it possible to add a icon symbol to a polygon

I am currently working on a openlayers 3 project and for better visulaizing i need to show both. The Polygon shape(attribute based color) which works great and an icon on the polygon position. I know that the polygon contains multiple coordinates and so its not so easy to define a position for the icon. Now i have some kind of workaround that creates an seperate overlay with the interior Points of the polygon to mark the position of the icons. To make the project more simple i want to combine these two styling. Does anyone know if its possible?
Kind Regards
I presumes that you use a ol.source.serversource for your data.
The trick is to test all your features for being a polygon. If it is, you create a point feature you add to your source.
First create the source and the layer:
var avlVectorSource = new ol.source.ServerVector({
format: new ol.format.GeoJSON(),
loader: function(extent, resolution, projection) {
myLoader(resolution);
}
});
var myLayer = new ol.layer.Vector({
source: myVectorSource,
style: myStyleFunction
});
The layer has a style function to set the right icon.
The main thing is the loader:
var myLoader = function(resolution){
$.ajax({
url: "http://myJsonSource.com",
timeout: 1000,
success: function(response) {
var layerJSONString = $.parseJSON(response);
var newFeatures = [];
j= 0;
var size=layerJSONString.features.length;
for (i = 0; i < size; i++){
var feat = layerJSONString.features[i];
var geom = feat.geometry;
var type = geom.type;
if(type == "Polygon")
{
var poly = new ol.geom.Polygon(geom.coordinates);
var extent = poly.getExtent();
var coord = [];
coord[0] = (extent[2]-extent[0])/2 + extent[0];
coord[1] = (extent[3]-extent[1])/2 + extent[1];
var point = new ol.geom.Point(coord);
newFeatures[j++] = new ol.Feature({
geometry : point,
StyleName : feat.properties.StyleName
});
}
}
avlVectorSource.addFeatures(myVectorSource.readFeatures(response));
avlVectorSource.addFeatures(newFeatures);
},
error: myLoadError
});
}
};
The documentation says that ol.geom.Polygon has a method called getInteriorPoint(). It has but I can get it to work. So I calculate the center point of the extent of the polygon.
I use "StyleName" to set the right icon in my style function.

Adding markers from a SQL db to OSM

I'm new to OpenStreetMap and I've been browsing the wiki and the net and I can't seem to find a tutorial anywhere but I've seen examples on the net.
Basically I want to generate my own OpenStreetmap and plot markers taking the latitude and longitude from a MySQL database and plotting them on my map. When the user clicks on a mark I'd like to have a popup. Basically I want this http://code.google.com/apis/maps/articles/phpsqlajax.html but for OpenStreetMap and not Google-maps.
Looks like they are using openLayer for map rendering. Here are some examples and api docs.
http://openlayers.org/dev/examples/
http://trac.osgeo.org/openlayers/wiki/Documentation
To accomplish this, you need to figure out the javascript for presenting markers on a "slippy map" interface.
OpenLayers is the name of a popular javascript library. It's free and open source. It's used to display a slippy map on the OpenStreetMap.org homepage, and various other sites around the web. It's often confused with OpenStreetMap itself, or people mistakenly get the impression that you must use OpenLayers if you want to embed OpenStreetMap maps on your site. Not true. There's lots of alternative javascript libraries for displaying a slippy map
...including the google maps API! You can set up a google maps display, but show OpenStreetMap "tile" images instead of google tiles. See Google Maps Example. This has the advantage of code compatibility, meaning you could follow through google maps tutorial you've linked there, but then drop in a cheeky bit of code to specify OpenStreetMap as the tile layer.
This has the disadvantage of showing an big evil google logo, and requiring an even more evil google maps API key, so all the cool kids are using OpenLayers.
There's various examples of using OpenLayers on the OpenStreetMap wiki. The "OpenLayers Dynamic POI" example shows the use of database for markers (although that example is a bit convoluted). Another popups example on my site
Hope that helps
// Sample code by August Li
// Modified by Tom Moore to work with SQL
var zoom, center, currentPopup, map, lyrMarkers;
var popupClass = OpenLayers.Class(OpenLayers.Popup.FramedCloud, {
"autoSize": true,
"minSize": new OpenLayers.Size(300, 50),
"maxSize": new OpenLayers.Size(500, 300),
"keepInMap": true
});
var bounds = new OpenLayers.Bounds();
var lat=36.287179515680556;
var lon=-96.69170379638672;
var zoom=11;
var lonLat = new OpenLayers.LonLat(lon, lat).transform(
new OpenLayers.Projection("EPSG:4326"), new OpenLayers.Projection("EPSG:900913"));
// begin addMarker function
// info1 I was going to use to add a tooltip. Haven't figured out
// how to do that in OpenLayers yet :( Someone who knows share that with us
// iconurl is the url to the png file that you want to use for the icon.
// you MUST call addMarker at least once to initialize the map
function addMarker(lat, lng, info, info1, iconurl) {
// First check to see if the map has been initialized. If not, do that now ...
if (map == null) {
var options = {
projection: new OpenLayers.Projection("EPSG:900913"),
displayProjection: new OpenLayers.Projection("EPSG:4326"),
units: "m",
numZoomLevels: 19,
maxResolution: 156543.0339,
maxExtent: new OpenLayers.Bounds(-20037508.34, -20037508.34, 20037508.34, 20037508.34)
};
map = new OpenLayers.Map("map", options);
map.addControl(new OpenLayers.Control.PanZoomBar());
var lyrOsm = new OpenLayers.Layer.OSM();
map.addLayer(lyrOsm);
lyrMarkers = new OpenLayers.Layer.Markers("Markers");
map.addLayer(lyrMarkers);
//add marker on given coordinates
map.setCenter(lonLat, zoom);
zoom = map.getZoom();
}
var iconSize = new OpenLayers.Size(36, 36);
var iconOffset = new OpenLayers.Pixel(-(iconSize.w / 2), -iconSize.h);
var icon = new OpenLayers.Icon(iconurl, iconSize, iconOffset);
var pt = new OpenLayers.LonLat(lng, lat).transform(
new OpenLayers.Projection("EPSG:4326"), map.getProjectionObject());
bounds.extend(pt);
var feature = new OpenLayers.Feature(lyrMarkers, pt);
feature.closeBox = true;
feature.popupClass = popupClass;
feature.data.popupContentHTML = info;
feature.data.overflow = "auto";
var marker = new OpenLayers.Marker(pt, icon.clone());
var markerClick = function(evt) {
if (currentPopup != null && currentPopup.visible()) {
currentPopup.hide();
}
if (this.popup == null) {
this.popup = this.createPopup(this.closeBox);
map.addPopup(this.popup);
this.popup.show();
} else {
this.popup.toggle();
}
currentPopup = this.popup;
OpenLayers.Event.stop(evt);
};
marker.events.register("mousedown", feature, markerClick);
lyrMarkers.addMarker(marker);
}
// end addMarker function
Best regards! I hope this helps someone who needs this to work...

Resources