feat: fontnik和sprite工具修改,osm-bright-style使用
This commit is contained in:
parent
c7b41ff7aa
commit
a4f49c7ee0
|
@ -64,6 +64,10 @@ func NewAPI(e *echo.Echo, s *store.Store) {
|
|||
api.GET("/swagger", func(c echo.Context) error {
|
||||
return c.Redirect(http.StatusMovedPermanently, "/swagger/index.html")
|
||||
})
|
||||
|
||||
// style
|
||||
api.Static("/style", "./style")
|
||||
|
||||
api.Register()
|
||||
|
||||
for _, route := range api.Routes() {
|
||||
|
|
6
main.go
6
main.go
|
@ -32,7 +32,7 @@ func testSprite() {
|
|||
panic(err)
|
||||
}
|
||||
|
||||
spriteBuilder.Build()
|
||||
spriteBuilder.Build("icons")
|
||||
}
|
||||
|
||||
func testFont() {
|
||||
|
@ -46,7 +46,9 @@ func testFont() {
|
|||
panic(err)
|
||||
}
|
||||
|
||||
fonts.ToPBF()
|
||||
fonts.Build("Noto Sans Bold")
|
||||
fonts.Build("Noto Sans Italic")
|
||||
fonts.Build("Noto Sans Regular")
|
||||
}
|
||||
|
||||
func testTiles() {
|
||||
|
|
|
@ -4,9 +4,11 @@ import (
|
|||
"fmt"
|
||||
"image"
|
||||
"image/draw"
|
||||
"log/slog"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"git.zhouxhere.com/zhouxhere/maptile/protobuf"
|
||||
|
@ -38,8 +40,10 @@ type FontFace struct {
|
|||
}
|
||||
|
||||
type Fontnik struct {
|
||||
outPath string
|
||||
FontFaces []*FontFace
|
||||
outPath string
|
||||
// Name string
|
||||
// FontFaces []*FontFace
|
||||
FontMaps map[string][]*FontFace
|
||||
}
|
||||
|
||||
func NewFontnik(fontPath, outPath string) (*Fontnik, error) {
|
||||
|
@ -58,110 +62,122 @@ func NewFontnik(fontPath, outPath string) (*Fontnik, error) {
|
|||
|
||||
files, err := os.ReadDir(fontPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return nil, fmt.Errorf("读取字体目录失败: %v", err)
|
||||
}
|
||||
|
||||
fontFaces := []*FontFace{}
|
||||
fontMap := make(map[string][]*FontFace)
|
||||
|
||||
for _, file := range files {
|
||||
if file.IsDir() {
|
||||
continue
|
||||
}
|
||||
filePath := filepath.Join(fontPath, file.Name())
|
||||
|
||||
if !strings.HasSuffix(file.Name(), ".ttf") && !strings.HasSuffix(file.Name(), ".otf") {
|
||||
// return nil, errors.Errorf("font file not support %s", file.Name())
|
||||
if file.IsDir() {
|
||||
faces := readFonts(filePath)
|
||||
fontMap[file.Name()] = faces
|
||||
continue
|
||||
}
|
||||
|
||||
fontBytes, err := os.ReadFile(filePath)
|
||||
fontFace, err := readFont(filePath)
|
||||
if err != nil {
|
||||
// return nil, fmt.Errorf("读取字体文件失败: %v", err)
|
||||
slog.Error("Failed to read font file %s: %v", filePath, err)
|
||||
continue
|
||||
}
|
||||
|
||||
openFont, err := opentype.Parse(fontBytes)
|
||||
if err != nil {
|
||||
// return nil, fmt.Errorf("解析字体失败: %v", err)
|
||||
continue
|
||||
}
|
||||
fileName := strings.TrimSuffix(file.Name(), filepath.Ext(file.Name()))
|
||||
|
||||
fontFamily, err := openFont.Name(&sfnt.Buffer{}, sfnt.NameIDFamily)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fontSubFamily, err := openFont.Name(&sfnt.Buffer{}, sfnt.NameIDSubfamily)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fontFamily = fmt.Sprintf("%s %s", fontFamily, fontSubFamily)
|
||||
|
||||
face, err := opentype.NewFace(openFont, &opentype.FaceOptions{
|
||||
Size: DefaultFontSize,
|
||||
DPI: 72,
|
||||
Hinting: font.HintingFull,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
metrics := face.Metrics()
|
||||
// fontDesignedHeight := metrics.Ascent.Floor() + metrics.Descent.Floor()
|
||||
// fixed := int(math.Round(float64(metrics.Height.Floor()-fontDesignedHeight)/2)) + 1
|
||||
|
||||
fontFaces = append(fontFaces, &FontFace{
|
||||
Name: fontFamily,
|
||||
Font: face,
|
||||
// yStart: metrics.Height.Floor() + metrics.Descent.Floor() + fixed,
|
||||
yStart: metrics.Ascent.Floor(),
|
||||
// maxCode: openFont.NumGlyphs(),
|
||||
maxCode: DefaultMaxCode,
|
||||
})
|
||||
fontMap[fileName] = append(fontMap[fileName], fontFace)
|
||||
}
|
||||
|
||||
return &Fontnik{
|
||||
outPath: filepath.Join(filepath.Dir(basePath), outPath),
|
||||
FontFaces: fontFaces,
|
||||
outPath: filepath.Join(filepath.Dir(basePath), outPath),
|
||||
// FontFaces: fontFaces,
|
||||
FontMaps: fontMap,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f *Fontnik) ToPBF() {
|
||||
func (f *Fontnik) Build(name string) error {
|
||||
if _, ok := f.FontMaps[name]; !ok {
|
||||
return fmt.Errorf("font %s not found", name)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(f.outPath); err != nil {
|
||||
err = os.MkdirAll(f.outPath, 0644)
|
||||
path := filepath.Join(f.outPath, name)
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
err = os.MkdirAll(path, 0644)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, face := range f.FontFaces {
|
||||
var errs []error
|
||||
|
||||
if _, err := os.Stat(filepath.Join(f.outPath, face.Name)); err != nil {
|
||||
err = os.MkdirAll(filepath.Join(f.outPath, face.Name), 0644)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
for start := 0; start <= DefaultMaxCode; start += DefaultBlockSize {
|
||||
|
||||
endCode := min(start+DefaultBlockSize-1, DefaultMaxCode)
|
||||
|
||||
stackRange := fmt.Sprintf("%d-%d", start, endCode)
|
||||
fontstack := &protobuf.Fontstack{
|
||||
Name: &name,
|
||||
Range: &stackRange,
|
||||
Glyphs: []*protobuf.Glyph{},
|
||||
}
|
||||
for _, face := range f.FontMaps[name] {
|
||||
if face.maxCode < start {
|
||||
continue
|
||||
}
|
||||
|
||||
tmpGlyphs, err := face.ProcessRange(start, endCode)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
slog.Error("Failed to process font %s range: %v", name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
fontstack.Glyphs = append(fontstack.Glyphs, tmpGlyphs...)
|
||||
}
|
||||
|
||||
fmt.Println("Font:", face.Name, "MaxCode:", face.maxCode)
|
||||
if len(f.FontMaps[name]) > 1 {
|
||||
sort.Slice(fontstack.Glyphs, func(i, j int) bool {
|
||||
return fontstack.Glyphs[i].GetId() < fontstack.Glyphs[j].GetId()
|
||||
})
|
||||
}
|
||||
|
||||
for start := 0; start <= int(face.maxCode); start += DefaultBlockSize {
|
||||
|
||||
endCode := min(start+DefaultBlockSize-1, face.maxCode)
|
||||
|
||||
stack, err := face.ProcessRange(start, endCode)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := f.SaveStack(stack); err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
if err := f.SaveStack(fontstack); err != nil {
|
||||
errs = append(errs, err)
|
||||
slog.Error("Failed to save font %s stack: %v", name, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return fmt.Errorf("build font %s failed: %v", name, errs)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
// for _, face := range f.FontFaces {
|
||||
|
||||
// if _, err := os.Stat(filepath.Join(f.outPath, face.Name)); err != nil {
|
||||
// err = os.MkdirAll(filepath.Join(f.outPath, face.Name), 0644)
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// }
|
||||
|
||||
// fmt.Println("Font:", face.Name, "MaxCode:", face.maxCode)
|
||||
|
||||
// for start := 0; start <= int(face.maxCode); start += DefaultBlockSize {
|
||||
|
||||
// endCode := min(start+DefaultBlockSize-1, face.maxCode)
|
||||
|
||||
// stack, err := face.ProcessRange(start, endCode)
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
|
||||
// if err := f.SaveStack(stack); err != nil {
|
||||
// fmt.Println(err)
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
func (f *Fontnik) SaveStack(stack *protobuf.Fontstack) error {
|
||||
|
@ -178,24 +194,26 @@ func (f *Fontnik) SaveStack(stack *protobuf.Fontstack) error {
|
|||
return os.WriteFile(filename, data, 0644)
|
||||
}
|
||||
|
||||
func (ff *FontFace) ProcessRange(start, end int) (*protobuf.Fontstack, error) {
|
||||
fontRange := fmt.Sprintf("%d-%d", start, end)
|
||||
stack := &protobuf.Fontstack{
|
||||
Name: &ff.Name,
|
||||
Range: &fontRange,
|
||||
Glyphs: []*protobuf.Glyph{},
|
||||
}
|
||||
func (ff *FontFace) ProcessRange(start, end int) ([]*protobuf.Glyph, error) {
|
||||
// fontRange := fmt.Sprintf("%d-%d", start, end)
|
||||
// stack := &protobuf.Fontstack{
|
||||
// Name: &ff.Name,
|
||||
// Range: &fontRange,
|
||||
// Glyphs: []*protobuf.Glyph{},
|
||||
// }
|
||||
glyphs := []*protobuf.Glyph{}
|
||||
|
||||
for code := start; code <= end; code++ {
|
||||
|
||||
glyph := ff.RenderGlyph(rune(code))
|
||||
|
||||
if glyph != nil {
|
||||
stack.Glyphs = append(stack.Glyphs, glyph)
|
||||
// stack.Glyphs = append(stack.Glyphs, glyph)
|
||||
glyphs = append(glyphs, glyph)
|
||||
}
|
||||
}
|
||||
|
||||
return stack, nil
|
||||
return glyphs, nil
|
||||
}
|
||||
|
||||
func (ff *FontFace) RenderGlyph(code rune) *protobuf.Glyph {
|
||||
|
@ -367,3 +385,82 @@ func edt1d(f []float64, d []float64, v []float64, z []float64, n int) {
|
|||
d[q] = (float64(q)-v[k])*(float64(q)-v[k]) + f[int(v[k])]
|
||||
}
|
||||
}
|
||||
|
||||
func readFonts(path string) []*FontFace {
|
||||
var files []string
|
||||
ttfFiles, err := filepath.Glob(path + "/*.ttf")
|
||||
if err != nil {
|
||||
slog.Error("Failed to read ttf files from path %s: %v", path, err)
|
||||
}
|
||||
files = append(files, ttfFiles...)
|
||||
otfFiles, err := filepath.Glob(path + "/*.otf")
|
||||
if err != nil {
|
||||
slog.Error("Failed to read otf files from path %s: %v", path, err)
|
||||
}
|
||||
files = append(files, otfFiles...)
|
||||
|
||||
var fontFaces []*FontFace
|
||||
|
||||
for _, file := range files {
|
||||
|
||||
fontFace, err := readFont(file)
|
||||
if err != nil {
|
||||
slog.Error("Failed to read font file %s: %v", file, err)
|
||||
continue
|
||||
}
|
||||
|
||||
fontFaces = append(fontFaces, fontFace)
|
||||
}
|
||||
|
||||
return fontFaces
|
||||
}
|
||||
|
||||
func readFont(path string) (*FontFace, error) {
|
||||
if !strings.HasSuffix(path, ".ttf") && !strings.HasSuffix(path, ".otf") {
|
||||
return nil, fmt.Errorf("unsupported font format: %s", path)
|
||||
}
|
||||
|
||||
fontBytes, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
openFont, err := opentype.Parse(fontBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fontFamily, err := openFont.Name(&sfnt.Buffer{}, sfnt.NameIDFamily)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fontSubFamily, err := openFont.Name(&sfnt.Buffer{}, sfnt.NameIDSubfamily)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fontFamily = fmt.Sprintf("%s %s", fontFamily, fontSubFamily)
|
||||
face, err := opentype.NewFace(openFont, &opentype.FaceOptions{
|
||||
Size: DefaultFontSize,
|
||||
DPI: 72,
|
||||
Hinting: font.HintingFull,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
metrics := face.Metrics()
|
||||
// fontDesignedHeight := metrics.Ascent.Floor() + metrics.Descent.Floor()
|
||||
// fixed := int(math.Round(float64(metrics.Height.Floor()-fontDesignedHeight)/2)) + 1
|
||||
|
||||
return &FontFace{
|
||||
Name: fontFamily,
|
||||
Font: face,
|
||||
// yStart: metrics.Height.Floor() + metrics.Descent.Floor() + fixed,
|
||||
yStart: metrics.Ascent.Floor(),
|
||||
// maxCode: openFont.NumGlyphs(),
|
||||
maxCode: DefaultMaxCode,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ func NewSpriteBuilder(iconPath string, outPath string) (*SpriteBuilder, error) {
|
|||
|
||||
files, err := os.ReadDir(iconPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sb := &SpriteBuilder{
|
||||
|
@ -113,27 +113,58 @@ func NewSpriteBuilder(iconPath string, outPath string) (*SpriteBuilder, error) {
|
|||
return sb, nil
|
||||
}
|
||||
|
||||
func (sb *SpriteBuilder) Build() error {
|
||||
for _, sprite := range sb.sprites {
|
||||
|
||||
if _, err := os.Stat(filepath.Join(sb.outPath, sprite.name)); err != nil {
|
||||
err = os.MkdirAll(filepath.Join(sb.outPath, sprite.name), 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func (sb *SpriteBuilder) Build(name string) error {
|
||||
// 查找name为name的sprite
|
||||
var sprite *Sprite
|
||||
for _, s := range sb.sprites {
|
||||
if s.name == name {
|
||||
sprite = &s
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
sprite.Fit()
|
||||
if sprite == nil {
|
||||
return fmt.Errorf("sprite %s not found", name)
|
||||
}
|
||||
|
||||
if err := sprite.Save(sb.outPath, 1); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := sprite.Save(sb.outPath, 2); err != nil {
|
||||
if _, err := os.Stat(filepath.Join(sb.outPath, sprite.name)); err != nil {
|
||||
err = os.MkdirAll(filepath.Join(sb.outPath, sprite.name), 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
sprite.Fit()
|
||||
|
||||
if err := sprite.Save(sb.outPath, 1); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := sprite.Save(sb.outPath, 2); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
// for _, sprite := range sb.sprites {
|
||||
|
||||
// if _, err := os.Stat(filepath.Join(sb.outPath, sprite.name)); err != nil {
|
||||
// err = os.MkdirAll(filepath.Join(sb.outPath, sprite.name), 0644)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
|
||||
// sprite.Fit()
|
||||
|
||||
// if err := sprite.Save(sb.outPath, 1); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// if err := sprite.Save(sb.outPath, 2); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
// return nil
|
||||
}
|
||||
|
||||
func (s *Sprite) Fit() {
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
<script setup lang="ts">
|
||||
import { json } from "@codemirror/lang-json";
|
||||
import {
|
||||
defaultHighlightStyle,
|
||||
syntaxHighlighting,
|
||||
} from "@codemirror/language";
|
||||
import { EditorState } from "@codemirror/state";
|
||||
import { basicSetup, EditorView } from "codemirror";
|
||||
|
||||
const { codes } = defineProps<{ codes: object | undefined }>();
|
||||
|
||||
const emit = defineEmits(["update:codes"]);
|
||||
|
||||
const view = ref<EditorView>();
|
||||
|
||||
watchEffect(() => {
|
||||
if (!view.value) return;
|
||||
view.value.dispatch({
|
||||
changes: {
|
||||
from: 0,
|
||||
to: view.value.state.doc.length,
|
||||
insert: JSON.stringify(codes, null, 2),
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
const state = EditorState.create({
|
||||
doc: codes ? JSON.stringify(codes, null, 2) : "",
|
||||
extensions: [
|
||||
basicSetup,
|
||||
syntaxHighlighting(defaultHighlightStyle),
|
||||
json(),
|
||||
EditorState.transactionFilter.of((tr) => {
|
||||
if (tr.docChanged) {
|
||||
// emit("update:codes", tr.newDoc.toJSON());
|
||||
debouncedUpdate(JSON.parse(tr.newDoc.toString()));
|
||||
}
|
||||
return tr;
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
view.value = new EditorView({
|
||||
parent: document.getElementById("code") as HTMLElement,
|
||||
state: state,
|
||||
});
|
||||
});
|
||||
|
||||
const debouncedUpdate = useDebounceFn((value: object) => {
|
||||
emit("update:codes", value);
|
||||
}, 1000 * 3);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="code"></div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
:global(.cm-editor) {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,54 @@
|
|||
<script setup lang="ts">
|
||||
import type { StyleSpecification } from "maplibre-gl";
|
||||
|
||||
const { mapStyle } = defineProps<{
|
||||
mapStyle: StyleSpecification & { id: string };
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "update:mapStyle", mapStyle: StyleSpecification & { id: string }): void;
|
||||
}>();
|
||||
|
||||
const updateMapStyle = (mapStyle: StyleSpecification & { id: string }) => {
|
||||
emit("update:mapStyle", mapStyle);
|
||||
};
|
||||
|
||||
const sources = computed(() => {
|
||||
return mapStyle.sources;
|
||||
});
|
||||
|
||||
const layers = computed(() => {
|
||||
return mapStyle.layers;
|
||||
});
|
||||
|
||||
const glyphs = computed(() => {
|
||||
return mapStyle.glyphs;
|
||||
});
|
||||
|
||||
const sprite = computed(() => {
|
||||
return mapStyle.sprite;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col">
|
||||
<div v-for="(source, name) in sources" class="flex flex-col">
|
||||
<h4>{{ name.toString() }}-{{ source.type }}</h4>
|
||||
<div class="flex flex-col" v-for="(value, key) in source">
|
||||
<div class="flex">
|
||||
<h6>{{ key }}:</h6>
|
||||
<p>{{ value }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="(layer, name) in layers" class="flex flex-col">
|
||||
<h4>{{ name.toString() }}-{{ layer.type }}</h4>
|
||||
<div class="flex flex-col" v-for="(value, key) in layer">
|
||||
<div class="flex">
|
||||
<h6>{{ key }}:</h6>
|
||||
<p>{{ value }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
|
@ -4,6 +4,9 @@ import "maplibre-gl/dist/maplibre-gl.css";
|
|||
import * as turf from "@turf/turf";
|
||||
import MapboxDraw from "@mapbox/mapbox-gl-draw";
|
||||
import "@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css";
|
||||
import Code from "./code.vue";
|
||||
import Editor from "./editor.vue";
|
||||
import { StyleSpecification } from "maplibre-gl";
|
||||
|
||||
const mapContainer = useTemplateRef("mapContainer");
|
||||
|
||||
|
@ -24,12 +27,23 @@ MapboxDraw.lib.theme.find((x) => x.id === "gl-draw-lines").paint[
|
|||
"line-dasharray"
|
||||
] = [0.2, 2];
|
||||
|
||||
/**
|
||||
* @type {Ref<StyleSpecification & {id string}>}
|
||||
*/
|
||||
const styleJSON = ref({
|
||||
version: 8,
|
||||
sources: {},
|
||||
layers: [],
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
styleJSON.value = await fetch("/style.json").then((res) => res.json());
|
||||
if (!mapContainer.value) return;
|
||||
map.value = new maplibregl.Map({
|
||||
container: mapContainer.value, // container id
|
||||
// style: "https://demotiles.maplibre.org/style.json", // style URL
|
||||
style: "/style.json",
|
||||
// style: "/style.json",
|
||||
style: styleJSON.value,
|
||||
center: [120.147376, 30.272934], // starting position [lng, lat]
|
||||
zoom: 7, // starting zoom
|
||||
});
|
||||
|
@ -73,17 +87,17 @@ onUnmounted(() => {
|
|||
const featureResult = await feature.list({ page: 1, size: 10 });
|
||||
|
||||
const handleData = () => {
|
||||
// if (featureResult.status.value !== "success") return;
|
||||
// let jsonData = turf.featureCollection(
|
||||
// featureResult.data.value.data.map((x) =>
|
||||
// turf.feature(x.geometry, {
|
||||
// id: x.id,
|
||||
// name: x.name,
|
||||
// })
|
||||
// )
|
||||
// );
|
||||
if (featureResult.status.value !== "success") return;
|
||||
let jsonData = turf.featureCollection(
|
||||
featureResult.data.value.data.map((x) =>
|
||||
turf.feature(x.geometry, {
|
||||
id: x.id,
|
||||
name: x.name,
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
// map.value.addSource("data", { type: "geojson", data: jsonData });
|
||||
map.value.addSource("data", { type: "geojson", data: jsonData });
|
||||
// map.value.addLayer({
|
||||
// id: "data-symbol",
|
||||
// type: "symbol",
|
||||
|
@ -93,7 +107,6 @@ const handleData = () => {
|
|||
// },
|
||||
// layout: {
|
||||
// "text-field": ["get", "name"],
|
||||
// "text-font": ["Open Sans Regular", "Arial Unicode MS Regular"],
|
||||
// "text-size": 12,
|
||||
// },
|
||||
// });
|
||||
|
@ -107,97 +120,26 @@ const handleData = () => {
|
|||
// "fill-outline-color": "#FF0000", // 添加边界线颜色
|
||||
// },
|
||||
// });
|
||||
};
|
||||
|
||||
// map.value.addSource("zhejiang-pm", {
|
||||
// type: "vector",
|
||||
// url: "/api/v1/pmtiles/zhejiang-pm",
|
||||
// // url: "http://localhost:8080/zhejiang-pm.json"
|
||||
// });
|
||||
// map.value.addLayer({
|
||||
// id: "water",
|
||||
// type: "fill",
|
||||
// filter: ["==", "$type", "Polygon"],
|
||||
// source: "zhejiang-pm",
|
||||
// "source-layer": "water",
|
||||
// paint: {
|
||||
// "fill-color": "#80deea",
|
||||
// },
|
||||
// });
|
||||
// map.value.addLayer({
|
||||
// id: "water_river",
|
||||
// type: "line",
|
||||
// source: "zhejiang-pm",
|
||||
// "source-layer": "water",
|
||||
// minzoom: 9,
|
||||
// filter: ["in", "kind", "river"],
|
||||
// paint: {
|
||||
// "line-color": "#80deea",
|
||||
// "line-width": [
|
||||
// "interpolate",
|
||||
// ["exponential", 1.6],
|
||||
// ["zoom"],
|
||||
// 9,
|
||||
// 0,
|
||||
// 9.5,
|
||||
// 1,
|
||||
// 18,
|
||||
// 12,
|
||||
// ],
|
||||
// },
|
||||
// });
|
||||
// map.value.addLayer({
|
||||
// id: "world_cities",
|
||||
// type: "symbol",
|
||||
// minzoom: 0,
|
||||
// maxzoom: 6,
|
||||
// source: "world_cities",
|
||||
// "source-layer": "cities",
|
||||
// layout: {
|
||||
// "text-field": "{name}",
|
||||
// // "text-font": ["Open Sans Regular", "Arial Unicode MS Regular"],
|
||||
// "text-size": 12,
|
||||
// },
|
||||
// paint: {
|
||||
// "text-color": "rgb(255,0,0)",
|
||||
// },
|
||||
// });
|
||||
|
||||
// map.value.addSource("china-shortbread", {
|
||||
// type: "vector",
|
||||
// // url: "/api/v1/mbtiles/china-shortbread",
|
||||
// bounds: [73.41788, 14.27437, 134.8036, 53.65559],
|
||||
// maxzoom: 14,
|
||||
// minzoom: 0,
|
||||
// tiles: [
|
||||
// "http://localhost:8888/api/v1/mbtiles/china-shortbread/{z}/{x}/{y}",
|
||||
// ],
|
||||
// });
|
||||
|
||||
// map.value.addLayer({
|
||||
// id: "china-shortbread",
|
||||
// type: "symbol",
|
||||
// bounds: [73.41788, 14.27437, 134.8036, 53.65559],
|
||||
// maxzoom: 14,
|
||||
// minzoom: 0,
|
||||
// source: "china-shortbread",
|
||||
// "source-layer": "place_labels",
|
||||
// layout: {
|
||||
// "text-field": "hello",
|
||||
// // "text-font": ["Open Sans Regular", "Arial Unicode MS Regular"],
|
||||
// "text-size": 12,
|
||||
// },
|
||||
// paint: {
|
||||
// "text-color": "#000",
|
||||
// },
|
||||
// });
|
||||
|
||||
setTimeout(() => {
|
||||
let styles = map.value.getStyle();
|
||||
console.log(styles);
|
||||
}, 1000);
|
||||
const handleCodesChange = (codes) => {
|
||||
if (!map.value) return;
|
||||
console.log(codes);
|
||||
map.value.setStyle(codes);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="mapContainer" class="w-full h-full" />
|
||||
<div class="w-full h-full flex justify-between">
|
||||
<Code
|
||||
class="h-full flex-[0_0_20%] overflow-hidden"
|
||||
:codes="styleJSON"
|
||||
@update:codes="handleCodesChange"
|
||||
></Code>
|
||||
<Editor
|
||||
class="h-full flex-[0_0_20%] overflow-x-hidden overflow-y-auto"
|
||||
:map-style="styleJSON"
|
||||
></Editor>
|
||||
<div ref="mapContainer" class="h-full flex-[0_0_50%]"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -28,7 +28,7 @@ const route = useRoute();
|
|||
</label>
|
||||
<div class="dropdown dropdown-end ml-auto mr-2">
|
||||
<div tabindex="0" role="button" class="avatar online placeholder sm-auto">
|
||||
<div class="bg-neutral text-neutral-content w-8 rounded-full">
|
||||
<div class="bg-neutral text-neutral-content w-8 rounded-full !flex items-center justify-center">
|
||||
<span class="text-sm">AI</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -76,9 +76,9 @@ const route = useRoute();
|
|||
<main
|
||||
class="drawer-content flex-auto relative p-4 pb-0 overflow-hidden bg-base-100 flex flex-col"
|
||||
>
|
||||
<Breadcrumbs />
|
||||
<Breadcrumbs class="flex-none" />
|
||||
<section class="flex-auto overflow-y-auto overflow-x-hidden">
|
||||
<slot />
|
||||
<slot></slot>
|
||||
</section>
|
||||
<footer class="mt-auto w-full h-8 py-2 text-center text-xs">
|
||||
©powered by maptile
|
||||
|
|
|
@ -7,9 +7,13 @@
|
|||
"name": "nuxt-app",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@codemirror/lang-json": "^6.0.1",
|
||||
"@codemirror/language": "^6.11.1",
|
||||
"@codemirror/state": "^6.5.2",
|
||||
"@mapbox/mapbox-gl-draw": "^1.5.0",
|
||||
"@turf/turf": "^7.2.0",
|
||||
"@vueuse/nuxt": "^12.7.0",
|
||||
"codemirror": "^6.0.1",
|
||||
"maplibre-gl": "^5.1.1",
|
||||
"nuxt": "^3.16.2",
|
||||
"vue": "latest",
|
||||
|
@ -484,6 +488,97 @@
|
|||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/autocomplete": {
|
||||
"version": "6.18.6",
|
||||
"resolved": "https://registry.npmmirror.com/@codemirror/autocomplete/-/autocomplete-6.18.6.tgz",
|
||||
"integrity": "sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.17.0",
|
||||
"@lezer/common": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/commands": {
|
||||
"version": "6.8.1",
|
||||
"resolved": "https://registry.npmmirror.com/@codemirror/commands/-/commands-6.8.1.tgz",
|
||||
"integrity": "sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.4.0",
|
||||
"@codemirror/view": "^6.27.0",
|
||||
"@lezer/common": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/lang-json": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/@codemirror/lang-json/-/lang-json-6.0.1.tgz",
|
||||
"integrity": "sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@lezer/json": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/language": {
|
||||
"version": "6.11.1",
|
||||
"resolved": "https://registry.npmmirror.com/@codemirror/language/-/language-6.11.1.tgz",
|
||||
"integrity": "sha512-5kS1U7emOGV84vxC+ruBty5sUgcD0te6dyupyRVG2zaSjhTDM73LhVKUtVwiqSe6QwmEoA4SCiU8AKPFyumAWQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.23.0",
|
||||
"@lezer/common": "^1.1.0",
|
||||
"@lezer/highlight": "^1.0.0",
|
||||
"@lezer/lr": "^1.0.0",
|
||||
"style-mod": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/lint": {
|
||||
"version": "6.8.5",
|
||||
"resolved": "https://registry.npmmirror.com/@codemirror/lint/-/lint-6.8.5.tgz",
|
||||
"integrity": "sha512-s3n3KisH7dx3vsoeGMxsbRAgKe4O1vbrnKBClm99PU0fWxmxsx5rR2PfqQgIt+2MMJBHbiJ5rfIdLYfB9NNvsA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.35.0",
|
||||
"crelt": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/search": {
|
||||
"version": "6.5.11",
|
||||
"resolved": "https://registry.npmmirror.com/@codemirror/search/-/search-6.5.11.tgz",
|
||||
"integrity": "sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.0.0",
|
||||
"crelt": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/state": {
|
||||
"version": "6.5.2",
|
||||
"resolved": "https://registry.npmmirror.com/@codemirror/state/-/state-6.5.2.tgz",
|
||||
"integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@marijn/find-cluster-break": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/view": {
|
||||
"version": "6.37.1",
|
||||
"resolved": "https://registry.npmmirror.com/@codemirror/view/-/view-6.37.1.tgz",
|
||||
"integrity": "sha512-Qy4CAUwngy/VQkEz0XzMKVRcckQuqLYWKqVpDDDghBe5FSXSqfVrJn49nw3ePZHxRUz4nRmb05Lgi+9csWo4eg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.5.0",
|
||||
"crelt": "^1.0.6",
|
||||
"style-mod": "^4.1.0",
|
||||
"w3c-keyname": "^2.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@colors/colors": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmmirror.com/@colors/colors/-/colors-1.6.0.tgz",
|
||||
|
@ -1174,6 +1269,41 @@
|
|||
"integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@lezer/common": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmmirror.com/@lezer/common/-/common-1.2.3.tgz",
|
||||
"integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@lezer/highlight": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/@lezer/highlight/-/highlight-1.2.1.tgz",
|
||||
"integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@lezer/common": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@lezer/json": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/@lezer/json/-/json-1.0.3.tgz",
|
||||
"integrity": "sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@lezer/common": "^1.2.0",
|
||||
"@lezer/highlight": "^1.0.0",
|
||||
"@lezer/lr": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@lezer/lr": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmmirror.com/@lezer/lr/-/lr-1.4.2.tgz",
|
||||
"integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@lezer/common": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@mapbox/geojson-area": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmmirror.com/@mapbox/geojson-area/-/geojson-area-0.2.2.tgz",
|
||||
|
@ -1339,6 +1469,12 @@
|
|||
"integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@marijn/find-cluster-break": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz",
|
||||
"integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@napi-rs/wasm-runtime": {
|
||||
"version": "0.2.9",
|
||||
"resolved": "https://registry.npmmirror.com/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.9.tgz",
|
||||
|
@ -7343,6 +7479,21 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/codemirror": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/codemirror/-/codemirror-6.0.1.tgz",
|
||||
"integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.0.0",
|
||||
"@codemirror/commands": "^6.0.0",
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/lint": "^6.0.0",
|
||||
"@codemirror/search": "^6.0.0",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/color": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/color/-/color-3.2.1.tgz",
|
||||
|
@ -7597,6 +7748,12 @@
|
|||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/crelt": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/crelt/-/crelt-1.0.6.tgz",
|
||||
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cron-parser": {
|
||||
"version": "4.9.0",
|
||||
"resolved": "https://registry.npmmirror.com/cron-parser/-/cron-parser-4.9.0.tgz",
|
||||
|
@ -13715,6 +13872,12 @@
|
|||
"integrity": "sha512-FL8EeKFFyNQv5cMnXI31CIMCsFarSVI2bF0U0ImeNE3g/F1IvJQyqzOXxPBRXiwQfyBTlbNe88jh1jFW0O/jiQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/style-mod": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/style-mod/-/style-mod-4.1.2.tgz",
|
||||
"integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/stylehacks": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/stylehacks/-/stylehacks-7.0.4.tgz",
|
||||
|
@ -14957,6 +15120,12 @@
|
|||
"vue": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/w3c-keyname": {
|
||||
"version": "2.2.8",
|
||||
"resolved": "https://registry.npmmirror.com/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
|
||||
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/web-streams-polyfill": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmmirror.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
|
||||
|
|
|
@ -10,9 +10,13 @@
|
|||
"postinstall": "nuxt prepare"
|
||||
},
|
||||
"dependencies": {
|
||||
"@codemirror/lang-json": "^6.0.1",
|
||||
"@codemirror/language": "^6.11.1",
|
||||
"@codemirror/state": "^6.5.2",
|
||||
"@mapbox/mapbox-gl-draw": "^1.5.0",
|
||||
"@turf/turf": "^7.2.0",
|
||||
"@vueuse/nuxt": "^12.7.0",
|
||||
"codemirror": "^6.0.1",
|
||||
"maplibre-gl": "^5.1.1",
|
||||
"nuxt": "^3.16.2",
|
||||
"vue": "latest",
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
layout: "default",
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full h-full relative">
|
||||
<Map />
|
||||
</div>
|
||||
<Map></Map>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
|
|
|
@ -24,11 +24,12 @@
|
|||
"sources": {
|
||||
"openmaptiles": {
|
||||
"type": "vector",
|
||||
"url": "/api/v1/pmtiles/zhejiang-pm"
|
||||
"url": "/api/v1/pmtiles/zhejiang-pm",
|
||||
"maxzoom": 14
|
||||
}
|
||||
},
|
||||
"sprite": "https://openmaptiles.github.io/osm-bright-gl-style/sprite",
|
||||
"glyphs": "https://api.maptiler.com/fonts/{fontstack}/{range}.pbf?key={key}",
|
||||
"sprite": "http://localhost:8080/style/sprite/icons/icons",
|
||||
"glyphs": "http://localhost:8080/style/font/{fontstack}/{range}.pbf",
|
||||
"layers": [
|
||||
{
|
||||
"id": "background",
|
||||
|
|
Loading…
Reference in New Issue