How can i draw geojson maps on gridlayer use canvas? - leaflet

I have already know how to draw map on leaflet tilelayer, but I'm trying to have a better efficiency, so now I practicing canvas with leaflet gridlayer,but the outcome wasn't I expected.I have no idea how to do it, hope someone can help me! Thanks!
Here's my code below:
var tiles = new L.GridLayer();
tiles.createTile = function (coords) {
var tile = L.DomUtil.create('canvas', 'leaflet-tile');
var ctx = tile.getContext('2d');
var size = this.getTileSize()
tile.width = size.x
tile.height = size.y
console.log(size.x);
// 将切片号乘以切片分辨率,默认为256pixel,得到切片左上角的绝对像素坐标
var nwPoint = coords.scaleBy(size)
// 根据绝对像素坐标,以及缩放层级,反投影得到其经纬度
var nw = map.unproject(nwPoint, coords.z)
//从该切片左上角开始画,画一个切片大小的无填充矩形
ctx.strokeRect(nwPoint.x, nwPoint.y, size.x, size.y)
ctx.fillStyle = 'black';
//x,y,z显示
ctx.fillText('x: ' + coords.x + ', y: ' + coords.y + ', zoom: ' + coords.z, 50, 60);
//经纬度坐标
if(nw.lng<121 && nw.lng>119){
ctx.fillText('lat: ' + nw.lat + ', lon: ' + nw.lng, 50, 80);
}
console.log(nw);
//线的颜色
ctx.strokeStyle = 'black';
//这是canvans的方法
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(size.x - 1, 0);
ctx.lineTo(size.x - 1, size.y - 1);
ctx.lineTo(0, size.y - 1);
ctx.closePath();
ctx.stroke();
// //
for (i in da.paths) {//svg casnvas
//console.log(da.paths[i].path);
context = da.paths[i].path;
p = new Path2D(context);
// console.log(p);
ctx.stroke(p);
ctx.fill(p);
ctx.fillStyle = 'lightpink';
}
return tile;
}
tiles.addTo(map)
Like this:
I really hope my map can fit the base map,
like this:
It is only a concept map, thank you!

Related

Cannot pitch canvas layer correctly in Mapbox GL JS

I am moving some code from Leaflet into Mapbox GL JS. I have a 2d animated canvas which is rendered correctly when viewed top-down (pitch 0), rotated (bearing whatever), but is skewed when the pitch isn't zero. I have spent the past few days trying to correct it without much success.
My code needs to be able to convert between screen dimensions and map coordinates. E.g. for a given pixel, what lat-lon results (with map.project([lon, lat]) ). Or for a lat-lon, what pixel does it correspond to (with map.unproject([x,y]) ). When the canvas is rendered with pitch 0, the latitudes are equally spaced (*enough) across the pixels and everything works as desired. The conversions occur correctly.
When pitch is > 0, the coordinate conversions appear to work as expected as well. However, I think Mapbox GL JS takes the canvas, and turns it into a WebGL texture. This texture is then somehow scaled/rotated which turns the previous rectangle canvas into a trapezoid.
I need to be able to include this trapezoid scaling or account for this distortion in the canvas calculations.
Rough Sample
The snippet below is an attempt at replicating what is going on with a far simpler codebase. It's based on https://docs.mapbox.com/mapbox-gl-js/example/canvas-source/. You'll see the circles moving around, turning into ovals at different points and stretching--they should remain circles. This is partly caused by the map.unproject() calls using the screen dimensions, which are used to determine the map bounds. In 2d space, this works well because the screen bounds are identical to the camera view. But I think in 3d space, the screen dimensions are distorted by the camera view, and so the relation is lost between screen--camera--map which causes the skew.
My goal is to either correct the skew, or somehow use the camera properties to create the bounds (and project/unproject), OR there's another way to solve this. Essentially I want what is on the canvas at particular coordinates to stay there and not moved when pitched.
Thank you!
const PITCH = 45; // Change to 0 to see uniform shapes
// Based on https://docs.mapbox.com/mapbox-gl-js/example/canvas-source/
mapboxgl.accessToken = 'pk.eyJ1IjoiYnVzaGZpcmVpbyIsImEiOiJjazU3NGwzNGEwM2QxM2VwcWJ0djk1amFyIn0.DP3giqn33iswMWzlclr9jg';
var map = new mapboxgl.Map({
container: 'map',
zoom: 5,
minZoom: 4,
center: [95.899147, 18.088694],
pitch: PITCH,
style: 'mapbox://styles/mapbox/streets-v11'
});
//Animation from https://javascript.tutorials24x7.com/blog/how-to-draw-animated-circles-in-html5-canvas
var canvas = document.getElementById('canvasID');
var ctx = canvas.getContext('2d');
var circles = [];
var radius = 20;
function Circle(x, y, dx, dy, radius, color) {
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
this.radius = radius;
this.draw = function () {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
ctx.strokeStyle = color;
ctx.stroke();
};
this.update = function () {
if (this.x + this.radius > 400 || this.x - this.radius < 0) {
this.dx = -this.dx;
}
if (this.y + this.radius > 400 || this.y - this.radius < 0) {
this.dy = -this.dy;
}
this.x += this.dx;
this.y += this.dy;
this.draw();
};
}
for (var i = 0; i < 5; i++) {
var color =
'#' +
(0x1000000 + Math.random() * 0xffffff).toString(16).substr(1, 6);
var x = Math.random() * (400 - radius * 2) + radius;
var y = Math.random() * (400 - radius * 2) + radius;
var dx = (Math.random() - 0.5) * 2;
var dy = (Math.random() - 0.5) * 2;
circles.push(new Circle(x, y, dx, dy, radius, color));
}
function animate() {
requestAnimationFrame(animate);
ctx.clearRect(0, 0, 400, 400);
for (var r = 0; r < 5; r++) {
circles[r].update();
}
}
animate();
let size = this.map.getContainer();
const nw = map.unproject([0, 0]);
const ne = map.unproject([size.clientWidth, 0]);
const se = map.unproject([size.clientWidth, size.clientHeight]);
const sw = map.unproject([0, size.clientHeight]);
map.on('load', function () {
map.addSource('canvas-source', {
type: 'canvas',
canvas: 'canvasID',
coordinates: [
[nw.lng, nw.lat],
[ne.lng, ne.lat],
[se.lng, se.lat],
[sw.lng, sw.lat],
],
animate: true
});
map.addLayer({
id: 'canvas-layer',
type: 'raster',
source: 'canvas-source'
});
});
body { margin: 0; padding: 0; }
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Canvas Demo</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
<link href="https://api.mapbox.com/mapbox-gl-js/v2.3.1/mapbox-gl.css" rel="stylesheet">
<script src="https://api.mapbox.com/mapbox-gl-js/v2.3.1/mapbox-gl.js"></script>
</head>
<body>
<canvas id="canvasID" width="400" height="400">Canvas not supported</canvas>
<div id="map"></div>
</body>
</html>

Present Sheet in a center of parent window

I use function
NSViewController.PresentViewControllerAsSheet(NSViewController)
and everything works great, but I would like to have a chance change the position of this sheet (for ex. in the center of parent).
How to do that ?
Ok, I have found solution:
C# code, Xamarin.Mac
controller.PresentViewControllerAsModalWindow(_settingsController);
if (_settingsController.View.Window != null)
{
var x = (_window.Frame.Left) + (_window.Frame.Width / 2) - 400;
var y = (_window.Frame.Top) + (_window.Frame.Height / 2) - 300;
_settingsController.View.Window.SetFrame(new CoreGraphics.CGRect(x, y, 800, 600), true);
_settingsController.View.Window.TitleVisibility = NSWindowTitleVisibility.Hidden;
_settingsController.View.Window.TitlebarAppearsTransparent = true;
_settingsController.View.Window.StyleMask = NSWindowStyle.FullSizeContentView | NSWindowStyle.Borderless;
_settingsController.View.Window.IsMovable = false;
}

Underlining with pdfnet results in different line thickness

The code that i use for underlining a selection of text. I begin calling the addUnderline() method, the other methods are helper methods.
private pdftron.SDF.Obj CreateUnderlineAppearance(pdftron.PDF.Rect bbox)
{
ElementBuilder builder = new ElementBuilder();
ElementWriter writer = new ElementWriter();
builder.PathBegin();
builder.MoveTo(bbox.x1, bbox.y1);
builder.LineTo(bbox.x2, bbox.y1);
Element line = builder.PathEnd();
//Set color attributes for the line...
line.SetPathFill(false);
line.SetPathStroke(true);
GState gs = line.GetGState();
gs.SetStrokeColorSpace(ColorSpace.CreateDeviceRGB());
gs.SetStrokeColor(new ColorPt(0, 0, 0)); // black
gs.SetLineWidth(2);
writer.Begin(m_document);
writer.WriteElement(line);
pdftron.SDF.Obj stm = writer.End();
builder.Dispose();
writer.Dispose();
// Set the bounding box
stm.PutRect("BBox", bbox.x1, bbox.y1, bbox.x2, bbox.y2);
stm.PutName("Subtype", "Form");
return stm;
}
public Annot CreateUnderlineAnnot(pdftron.PDF.Rect rect)
{
Annot underlineAnnot = Annot.Create(m_document, Annot.Type.e_Underline, rect);
underlineAnnot.SetAppearance(CreateUnderlineAppearance(rect));
return underlineAnnot;
}
public void AddUnderline()
{
if (m_document != null)
{
PDFViewCtrl.Selection selection = m_pdfViewer.GetSelection();
int pageNumber = selection.GetPageNum();
double[] quads = selection.GetQuads();
int numQuads = quads.Length / 8;
if (quads.Length % 8 == 0) //must have at least 8 points to be valid
{
Console.WriteLine("GetRectsFromQuads - numQuads: " + numQuads.ToString());
for (int i = 0; i < numQuads; i++)
{
Rect selectionRect = GetSelectionRect(ref quads, i);
//Console.WriteLine("GetRectsFromQuads - aRect: " + rectX1.ToString() + " | " + rectY1.ToString() + " | " + rectX2.ToString() + " | " + rectY2.ToString());
Annot underlineAnnot = CreateUnderlineAnnot(selectionRect);
m_pdfViewer.AddUnderlineAnnotationToPage(underlineAnnot, pageNumber);
//m_pdfViewer.Refresh(); --> to see how this algorithm works when debugging
}
m_pdfViewer.RefreshAnnotations();
}
}
}
You can see in the image if you look closely that some lines are thicker or thinner than others. Is this fixable? by the way, when i zoom in/out the problem is gone...
You need to set the following on your pdf view control:
PDFViewCtrl.SetThinLineAdjustment(true, true);
That will remove the aliasing on the lines, and mean all lines that are 1.5px are 1px, and so on. See here: https://www.pdftron.com/pdfnet/mobile/docs/WinRT/html/M_pdftron_PDF_PDFViewCtrl_SetThinLineAdjustment.htm

How to create a combo box(drop down list) with button controls for window form tool strip???

Like a layer control drop down in AutoCAD.
Thanks.
I solved it myself...
I create a user control which inherit the combo box class,this control works fine for me like a normal combo box.
Here is a code, that may help you to understand more about this control.
public partial class dropdown : ComboBox
{
public dropdown()
{
this.DrawMode = DrawMode.OwnerDrawVariable;
}
protected override void OnDrawItem(DrawItemEventArgs e)
{
// Make sure we're not trying to draw something that isn't there.
if (e.Index >= this.Items.Count || e.Index <= -1)
return;
// Get the item object.
object item = this.Items[e.Index];
if (item == null)
return;
// Draw the background color depending on
// if the item is selected or not.
if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
{
// The item is selected.
// We want a blue background color.
e.Graphics.FillRectangle(new SolidBrush(Color.Moccasin), e.Bounds);
}
else
{
// The item is NOT selected.
// We want a white background color.
e.Graphics.FillRectangle(new SolidBrush(Color.White), e.Bounds);
}
if ((e.State & DrawItemState.ComboBoxEdit) == DrawItemState.ComboBoxEdit)
{
e.Graphics.FillRectangle(new SolidBrush(Color.White), e.Bounds);
}
// Draw the items...
LayerEdit le = (LayerEdit)item;
string text = le.LayerName.ToString();
SizeF stringSize = e.Graphics.MeasureString(text, this.Font);
// Draw layer visible(on/off)...
e.Graphics.DrawImage(byteArrayToImage(le.Visible), 2, 1 + e.Bounds.Y + (e.Bounds.Height - stringSize.Height) / 2, 14, 14);
// Draw layer Lock...
e.Graphics.DrawImage(byteArrayToImage(le.Lock), 18, 1 + e.Bounds.Y + (e.Bounds.Height - stringSize.Height) / 2, 15, 14);
// Draw layer plot...
e.Graphics.DrawImage(byteArrayToImage(le.Plot), 37, 1 + e.Bounds.Y + (e.Bounds.Height - stringSize.Height) / 2, 14, 14);
// Draw layer color...
Brush brush = new SolidBrush(ColorTranslator.FromHtml(le.ColourValue));
e.Graphics.FillRectangle(brush, 55, 4 + e.Bounds.Y + (e.Bounds.Height - stringSize.Height) / 2, 10, 10);
e.Graphics.DrawRectangle(Pens.Black, 55, 4 + e.Bounds.Y + (e.Bounds.Height - stringSize.Height) / 2, 10, 10);
// Draw layer name...
e.Graphics.DrawString(text, this.Font, new SolidBrush(Color.Black), new PointF(68, e.Bounds.Y + (e.Bounds.Height - stringSize.Height) / 2));
}
//convert bytes into image...
public Image byteArrayToImage(byte[] byteArrayIn)
{
System.IO.MemoryStream ms = new System.IO.MemoryStream(byteArrayIn);
Image returnImage = Image.FromStream(ms);
return returnImage;
}
}

Making paths and images draggable in Raphael js

Is it possible to be able to drag and drop objects other than just circles and rectangles around a page using Raphael js?
I want to add in paths and images which you can then move around but its proving tricky.
I would like to work this out with Raphael because of its support with touch interfaces.
Here is the code
<script>
window.onload = function () {
var R = Raphael(0, 0, "100%", "100%"),
r = R.circle(100, 100, 50).attr({fill: "hsb(0, 1, 1)", stroke: "none", opacity: .5}),
g = R.circle(210, 100, 50).attr({fill: "hsb(.3, 1, 1)", stroke: "none", opacity: .5}),
b = R.circle(320, 100, 50).attr({fill: "hsb(.6, 1, 1)", stroke: "#fff", "fill-opacity": 0, "stroke-width": 0.8, opacity: .5}),
p = R.path("M 250 250 l 0 -50 l -50 0 l 0 -50 l -50 0 l 0 50 l -50 0 l 0 50 z") .attr({fill: "hsb(.8, 1, 1)", stroke: "none", opacity: .5});
var start = function () {
this.ox = this.attr("cx");
this.oy = this.attr("cy");
this.animate({r: 70, opacity: .25}, 500, ">");
},
move = function (dx, dy) {
this.attr({cx: this.ox + dx, cy: this.oy + dy});
},
up = function () {
this.animate({r: 50, opacity: .5}, 500, ">");
};
R.set(r, g, b, p).drag(move, start, up);
};
</script>
The key here (that I found) is to convert the x and y deltas into translate values, which the path object understands.
http://www.nathancolgate.com/post/2946823151/drag-and-drop-paths-in-raphael-js
Effectively the same approach:
var paper = Raphael(10, 50, 320, 200);
var tri = paper.path("M0 0L0 20L25 10L0 0Z").attr("fill", "#ff0");
var rex = paper.rect(10, 20, 50, 50).attr("fill", "#ff0");
var start = function () {
this.odx = 0;
this.ody = 0;
this.animate({"fill-opacity": 0.2}, 500);
},
move = function (dx, dy) {
this.translate(dx - this.odx, dy - this.ody);
this.odx = dx;
this.ody = dy;
},
up = function () {
this.animate({"fill-opacity": 1}, 500);
};
tri.drag(move, start, up);
rex.drag(move, start, up);
As translate is being deprecated in Raphael, I've modified Nathan's answer to work with transform:
var paper = Raphael(10, 50, 320, 200);
var tri = paper.path("M0 0L0 20L25 10L0 0Z").attr("fill", "#ff0");
var start = function () {
this.lastdx ? this.odx += this.lastdx : this.odx = 0;
this.lastdy ? this.ody += this.lastdy : this.ody = 0;
this.animate({"fill-opacity": 0.2}, 500);
},
move = function (dx, dy) {
this.transform("T"+(dx+this.odx)+","+(dy+this.ody));
this.lastdx = dx;
this.lastdy = dy;
},
up = function () {
this.animate({"fill-opacity": 1}, 500);
};
tri.drag(move, start, up);
I'm relatively new to Raphael and came up with this through trial and error, so someone out there might have an explanation of why it works or a cleaner way of doing it ;)
I experimented with this a little while ago, and got it working using the following approach:
Add an initially hidden, styled, absolutely positioned div with a transparent background and suitable border styling to your page, and using jQuery/UI make it draggable.
Add a click event to each of the Rapahel/SVG elements you wish to be draggable, and in this event add code to resize and reposition the div over the element which has just been clicked and then make it visible.
Add code to the div which updates the position of the Raphael element when it is dragged.
I extended this to add resizing capabilities, and this also worked well, but going forward it would be great to see drag, drop and resize capabilities (ideally properly integrated into the library rather than using jQuery) built into Raphael, as these features would open up a whole bunch of possibilities for in-browser designers using pure Raphael.
Try this for non-circles. Circles attributes are different than images, text, etc, I think.
var start = function () {
this.ox = this.attr("x");
this.oy = this.attr("y");
this.animate({r: 70, opacity: .25}, 500, ">");
},
move = function (dx, dy) {
this.attr({x: this.ox + dx, y: this.oy + dy});
},
up = function () {
this.animate({r: 50, opacity: .5}, 500, ">");
};
I would recommend you raphael.draggable library, that makes the trick for you. I used it with a map application that allows the user to use zoom over the map and then drag it.
I had a problem with this library in IE8 because in the function events refering to mousedown, mousemove, etc. IE drops an exception, telling the user that event is null. You can solve it by replacing the event by e and adding e = e || event in the raphael.draggable.js script. This fix doesn't affect other browsers.
So, the method mousemove in the startDragger is:
function startDragger() {
document.onmousemove = function(e) {
e = e || event
if (paper.draggable.current()) {
var transX = e.clientX - lastDragX;
var transY = e.clientY - lastDragY;
paper.draggable.current().translate(transX, transY);
lastDragX = e.clientX;
lastDragY = e.clientY;
}
};
}
And the link:
https://github.com/mephraim/raphael.draggable
Hope this could help you.
it's not that hard if you understand the usual dragging functions Chris Butler gave you.
I use this:
var start = function () {
//storing original coordinates
this.xSource = this.attrs.path[0][1];
this.ySource = this.attrs.path[0][2];
this.xDest = this.attrs.path[1][1];
this.yDest = this.attrs.path[1][2];
this.attr({opacity: 0.5});
},
move = function (dx, dy) {
//move will be called with dx and dy
var xS = this.xSource+dx;
var xD = this.xDest+dx;
var yS = this.ySource+dy;
var yD = this.yDest+dy;
this.attr({path: "M"+ xS +" "+ yS +"L"+ xD +" "+yD});
},
drag = function(){
this.node.drag(this.move,this.start,this.up);
};
You can also know which sort of figure you're dragging in the functions with this.type, so that you can make these functions work for all sort of figures.
In case anyone is still looking for a solution, here's a plugin that scales, rotates and drags all shapes including paths.
https://github.com/ElbertF/Raphael.FreeTransform